Tento skript by nemal byť nainštalovaný priamo. Je to knižnica pre ďalšie skripty, ktorú by mali používať cez meta príkaz // @require https://update.greasyfork.org/scripts/371339/961539/GM_webextPref.js
- // ==UserScript==
- // @name gm-webext-pref
- // @version 0.4.2
- // @description A config library powered by webext-pref.
- // @license MIT
- // @author eight04 <eight04@gmail.com>
- // @homepageURL https://github.com/eight04/GM_webextPref
- // @supportURL https://github.com/eight04/GM_webextPref/issues
- // @namespace eight04.blogspot.com
- // @grant GM_getValue
- // @grant GM.getValue
- // @grant GM_setValue
- // @grant GM.setValue
- // @grant GM_deleteValue
- // @grant GM.deleteValue
- // @grant GM_addValueChangeListener
- // @grant GM_registerMenuCommand
- // @grant GM.registerMenuCommand
- // @include *
- // ==/UserScript==
-
- var GM_webextPref = (function () {
- 'use strict';
-
- /**
- * event-lite.js - Light-weight EventEmitter (less than 1KB when gzipped)
- *
- * @copyright Yusuke Kawasaki
- * @license MIT
- * @constructor
- * @see https://github.com/kawanet/event-lite
- * @see http://kawanet.github.io/event-lite/EventLite.html
- * @example
- * var EventLite = require("event-lite");
- *
- * function MyClass() {...} // your class
- *
- * EventLite.mixin(MyClass.prototype); // import event methods
- *
- * var obj = new MyClass();
- * obj.on("foo", function() {...}); // add event listener
- * obj.once("bar", function() {...}); // add one-time event listener
- * obj.emit("foo"); // dispatch event
- * obj.emit("bar"); // dispatch another event
- * obj.off("foo"); // remove event listener
- */
-
- function EventLite() {
- if (!(this instanceof EventLite)) return new EventLite();
- }
-
- const _module_ = {exports: {}};
- (function(EventLite) {
- // export the class for node.js
- if ("undefined" !== typeof _module_) _module_.exports = EventLite;
-
- // property name to hold listeners
- var LISTENERS = "listeners";
-
- // methods to export
- var methods = {
- on: on,
- once: once,
- off: off,
- emit: emit
- };
-
- // mixin to self
- mixin(EventLite.prototype);
-
- // export mixin function
- EventLite.mixin = mixin;
-
- /**
- * Import on(), once(), off() and emit() methods into target object.
- *
- * @function EventLite.mixin
- * @param target {Prototype}
- */
-
- function mixin(target) {
- for (var key in methods) {
- target[key] = methods[key];
- }
- return target;
- }
-
- /**
- * Add an event listener.
- *
- * @function EventLite.prototype.on
- * @param type {string}
- * @param func {Function}
- * @returns {EventLite} Self for method chaining
- */
-
- function on(type, func) {
- getListeners(this, type).push(func);
- return this;
- }
-
- /**
- * Add one-time event listener.
- *
- * @function EventLite.prototype.once
- * @param type {string}
- * @param func {Function}
- * @returns {EventLite} Self for method chaining
- */
-
- function once(type, func) {
- var that = this;
- wrap.originalListener = func;
- getListeners(that, type).push(wrap);
- return that;
-
- function wrap() {
- off.call(that, type, wrap);
- func.apply(this, arguments);
- }
- }
-
- /**
- * Remove an event listener.
- *
- * @function EventLite.prototype.off
- * @param [type] {string}
- * @param [func] {Function}
- * @returns {EventLite} Self for method chaining
- */
-
- function off(type, func) {
- var that = this;
- var listners;
- if (!arguments.length) {
- delete that[LISTENERS];
- } else if (!func) {
- listners = that[LISTENERS];
- if (listners) {
- delete listners[type];
- if (!Object.keys(listners).length) return off.call(that);
- }
- } else {
- listners = getListeners(that, type, true);
- if (listners) {
- listners = listners.filter(ne);
- if (!listners.length) return off.call(that, type);
- that[LISTENERS][type] = listners;
- }
- }
- return that;
-
- function ne(test) {
- return test !== func && test.originalListener !== func;
- }
- }
-
- /**
- * Dispatch (trigger) an event.
- *
- * @function EventLite.prototype.emit
- * @param type {string}
- * @param [value] {*}
- * @returns {boolean} True when a listener received the event
- */
-
- function emit(type, value) {
- var that = this;
- var listeners = getListeners(that, type, true);
- if (!listeners) return false;
- var arglen = arguments.length;
- if (arglen === 1) {
- listeners.forEach(zeroarg);
- } else if (arglen === 2) {
- listeners.forEach(onearg);
- } else {
- var args = Array.prototype.slice.call(arguments, 1);
- listeners.forEach(moreargs);
- }
- return !!listeners.length;
-
- function zeroarg(func) {
- func.call(that);
- }
-
- function onearg(func) {
- func.call(that, value);
- }
-
- function moreargs(func) {
- func.apply(that, args);
- }
- }
-
- /**
- * @ignore
- */
-
- function getListeners(that, type, readonly) {
- if (readonly && !that[LISTENERS]) return;
- var listeners = that[LISTENERS] || (that[LISTENERS] = {});
- return listeners[type] || (listeners[type] = []);
- }
-
- })(EventLite);
- var EventLite$1 = _module_.exports;
-
- function createPref(DEFAULT, sep = "/") {
- let storage;
- let currentScope = "global";
- let scopeList = ["global"];
- const events = new EventLite$1;
- const globalCache = {};
- let scopedCache = {};
- let currentCache = Object.assign({}, DEFAULT);
- let initializing;
-
- return Object.assign(events, {
- // storage,
- // ready,
- connect,
- disconnect,
- get,
- getAll,
- set,
- getCurrentScope,
- setCurrentScope,
- addScope,
- deleteScope,
- getScopeList,
- import: import_,
- export: export_,
- has
- });
-
- function import_(input) {
- const newScopeList = input.scopeList || scopeList.slice();
- const scopes = new Set(newScopeList);
- if (!scopes.has("global")) {
- throw new Error("invalid scopeList");
- }
- const changes = {
- scopeList: newScopeList
- };
- for (const [scopeName, scope] of Object.entries(input.scopes)) {
- if (!scopes.has(scopeName)) {
- continue;
- }
- for (const [key, value] of Object.entries(scope)) {
- if (DEFAULT[key] == undefined) {
- continue;
- }
- changes[`${scopeName}${sep}${key}`] = value;
- }
- }
- return storage.setMany(changes);
- }
-
- function export_() {
- const keys = [];
- for (const scope of scopeList) {
- keys.push(...Object.keys(DEFAULT).map(k => `${scope}${sep}${k}`));
- }
- keys.push("scopeList");
- return storage.getMany(keys)
- .then(changes => {
- const _scopeList = changes.scopeList || scopeList.slice();
- const scopes = new Set(_scopeList);
- const output = {
- scopeList: _scopeList,
- scopes: {}
- };
- for (const [key, value] of Object.entries(changes)) {
- const sepIndex = key.indexOf(sep);
- if (sepIndex < 0) {
- continue;
- }
- const scope = key.slice(0, sepIndex);
- const realKey = key.slice(sepIndex + sep.length);
- if (!scopes.has(scope)) {
- continue;
- }
- if (DEFAULT[realKey] == undefined) {
- continue;
- }
- if (!output.scopes[scope]) {
- output.scopes[scope] = {};
- }
- output.scopes[scope][realKey] = value;
- }
- return output;
- });
- }
-
- function connect(_storage) {
- storage = _storage;
- initializing = storage.getMany(
- Object.keys(DEFAULT).map(k => `global${sep}${k}`).concat(["scopeList"])
- )
- .then(updateCache);
- storage.on("change", updateCache);
- return initializing;
- }
-
- function disconnect() {
- storage.off("change", updateCache);
- storage = null;
- }
-
- function updateCache(changes, rebuildCache = false) {
- if (changes.scopeList) {
- scopeList = changes.scopeList;
- events.emit("scopeListChange", scopeList);
- if (!scopeList.includes(currentScope)) {
- return setCurrentScope("global");
- }
- }
- const changedKeys = new Set;
- for (const [key, value] of Object.entries(changes)) {
- const [scope, realKey] = key.startsWith(`global${sep}`) ? ["global", key.slice(6 + sep.length)] :
- key.startsWith(`${currentScope}${sep}`) ? [currentScope, key.slice(currentScope.length + sep.length)] :
- [null, null];
- if (!scope || DEFAULT[realKey] == null) {
- continue;
- }
- if (scope === "global") {
- changedKeys.add(realKey);
- globalCache[realKey] = value;
- }
- if (scope === currentScope) {
- changedKeys.add(realKey);
- scopedCache[realKey] = value;
- }
- }
- if (rebuildCache) {
- Object.keys(DEFAULT).forEach(k => changedKeys.add(k));
- }
- const realChanges = {};
- let isChanged = false;
- for (const key of changedKeys) {
- const value = scopedCache[key] != null ? scopedCache[key] :
- globalCache[key] != null ? globalCache[key] :
- DEFAULT[key];
- if (currentCache[key] !== value) {
- realChanges[key] = value;
- currentCache[key] = value;
- isChanged = true;
- }
- }
- if (isChanged) {
- events.emit("change", realChanges);
- }
- }
-
- function has(key) {
- return currentCache.hasOwnProperty(key);
- }
-
- function get(key) {
- return currentCache[key];
- }
-
- function getAll() {
- return Object.assign({}, currentCache);
- }
-
- function set(key, value) {
- return storage.setMany({
- [`${currentScope}${sep}${key}`]: value
- });
- }
-
- function getCurrentScope() {
- return currentScope;
- }
-
- function setCurrentScope(newScope) {
- if (currentScope === newScope) {
- return Promise.resolve(true);
- }
- if (!scopeList.includes(newScope)) {
- return Promise.resolve(false);
- }
- return storage.getMany(Object.keys(DEFAULT).map(k => `${newScope}${sep}${k}`))
- .then(changes => {
- currentScope = newScope;
- scopedCache = {};
- events.emit("scopeChange", currentScope);
- updateCache(changes, true);
- return true;
- });
- }
-
- function addScope(scope) {
- if (scopeList.includes(scope)) {
- return Promise.reject(new Error(`${scope} already exists`));
- }
- if (scope.includes(sep)) {
- return Promise.reject(new Error(`invalid word: ${sep}`));
- }
- return storage.setMany({
- scopeList: scopeList.concat([scope])
- });
- }
-
- function deleteScope(scope) {
- if (scope === "global") {
- return Promise.reject(new Error(`cannot delete global`));
- }
- return Promise.all([
- storage.setMany({
- scopeList: scopeList.filter(s => s != scope)
- }),
- storage.deleteMany(Object.keys(DEFAULT).map(k => `${scope}${sep}${k}`))
- ]);
- }
-
- function getScopeList() {
- return scopeList;
- }
- }
-
- const keys = Object.keys;
- function isBoolean(val) {
- return typeof val === "boolean"
- }
- function isElement(val) {
- return val && typeof val.nodeType === "number"
- }
- function isString(val) {
- return typeof val === "string"
- }
- function isNumber(val) {
- return typeof val === "number"
- }
- function isObject(val) {
- return typeof val === "object" ? val !== null : isFunction(val)
- }
- function isFunction(val) {
- return typeof val === "function"
- }
- function isArrayLike(obj) {
- return isObject(obj) && typeof obj.length === "number" && typeof obj.nodeType !== "number"
- }
- function forEach(value, fn) {
- if (!value) return
-
- for (const key of keys(value)) {
- fn(value[key], key);
- }
- }
- function isRef(maybeRef) {
- return isObject(maybeRef) && "current" in maybeRef
- }
-
- function _objectWithoutPropertiesLoose(source, excluded) {
- if (source == null) return {}
- var target = {};
- var sourceKeys = Object.keys(source);
- var key, i;
-
- for (i = 0; i < sourceKeys.length; i++) {
- key = sourceKeys[i];
- if (excluded.indexOf(key) >= 0) continue
- target[key] = source[key];
- }
-
- return target
- }
-
- const isUnitlessNumber = {
- animationIterationCount: 0,
- borderImageOutset: 0,
- borderImageSlice: 0,
- borderImageWidth: 0,
- boxFlex: 0,
- boxFlexGroup: 0,
- boxOrdinalGroup: 0,
- columnCount: 0,
- columns: 0,
- flex: 0,
- flexGrow: 0,
- flexPositive: 0,
- flexShrink: 0,
- flexNegative: 0,
- flexOrder: 0,
- gridArea: 0,
- gridRow: 0,
- gridRowEnd: 0,
- gridRowSpan: 0,
- gridRowStart: 0,
- gridColumn: 0,
- gridColumnEnd: 0,
- gridColumnSpan: 0,
- gridColumnStart: 0,
- fontWeight: 0,
- lineClamp: 0,
- lineHeight: 0,
- opacity: 0,
- order: 0,
- orphans: 0,
- tabSize: 0,
- widows: 0,
- zIndex: 0,
- zoom: 0,
- fillOpacity: 0,
- floodOpacity: 0,
- stopOpacity: 0,
- strokeDasharray: 0,
- strokeDashoffset: 0,
- strokeMiterlimit: 0,
- strokeOpacity: 0,
- strokeWidth: 0,
- };
-
- function prefixKey(prefix, key) {
- return prefix + key.charAt(0).toUpperCase() + key.substring(1)
- }
-
- const prefixes = ["Webkit", "ms", "Moz", "O"];
- keys(isUnitlessNumber).forEach((prop) => {
- prefixes.forEach((prefix) => {
- isUnitlessNumber[prefixKey(prefix, prop)] = 0;
- });
- });
-
- const SVGNamespace = "http://www.w3.org/2000/svg";
- const XLinkNamespace = "http://www.w3.org/1999/xlink";
- const XMLNamespace = "http://www.w3.org/XML/1998/namespace";
-
- function isVisibleChild(value) {
- return !isBoolean(value) && value != null
- }
-
- function className(value) {
- if (Array.isArray(value)) {
- return value.map(className).filter(Boolean).join(" ")
- } else if (isObject(value)) {
- return keys(value)
- .filter((k) => value[k])
- .join(" ")
- } else if (isVisibleChild(value)) {
- return "" + value
- } else {
- return ""
- }
- }
- const svg = {
- animate: 0,
- circle: 0,
- clipPath: 0,
- defs: 0,
- desc: 0,
- ellipse: 0,
- feBlend: 0,
- feColorMatrix: 0,
- feComponentTransfer: 0,
- feComposite: 0,
- feConvolveMatrix: 0,
- feDiffuseLighting: 0,
- feDisplacementMap: 0,
- feDistantLight: 0,
- feFlood: 0,
- feFuncA: 0,
- feFuncB: 0,
- feFuncG: 0,
- feFuncR: 0,
- feGaussianBlur: 0,
- feImage: 0,
- feMerge: 0,
- feMergeNode: 0,
- feMorphology: 0,
- feOffset: 0,
- fePointLight: 0,
- feSpecularLighting: 0,
- feSpotLight: 0,
- feTile: 0,
- feTurbulence: 0,
- filter: 0,
- foreignObject: 0,
- g: 0,
- image: 0,
- line: 0,
- linearGradient: 0,
- marker: 0,
- mask: 0,
- metadata: 0,
- path: 0,
- pattern: 0,
- polygon: 0,
- polyline: 0,
- radialGradient: 0,
- rect: 0,
- stop: 0,
- svg: 0,
- switch: 0,
- symbol: 0,
- text: 0,
- textPath: 0,
- tspan: 0,
- use: 0,
- view: 0,
- };
- function createElement(tag, attr, ...children) {
- if (isString(attr) || Array.isArray(attr)) {
- children.unshift(attr);
- attr = {};
- }
-
- attr = attr || {};
-
- if (!attr.namespaceURI && svg[tag] === 0) {
- attr = Object.assign({}, attr, {
- namespaceURI: SVGNamespace,
- });
- }
-
- if (attr.children != null && !children.length) {
- var _attr = attr
- ;({ children } = _attr);
- attr = _objectWithoutPropertiesLoose(_attr, ["children"]);
- }
-
- let node;
-
- if (isString(tag)) {
- node = attr.namespaceURI
- ? document.createElementNS(attr.namespaceURI, tag)
- : document.createElement(tag);
- attributes(attr, node);
- appendChild(children, node);
- } else if (isFunction(tag)) {
- if (isObject(tag.defaultProps)) {
- attr = Object.assign({}, tag.defaultProps, attr);
- }
-
- node = tag(
- Object.assign({}, attr, {
- children,
- })
- );
- }
-
- if (isRef(attr.ref)) {
- attr.ref.current = node;
- } else if (isFunction(attr.ref)) {
- attr.ref(node);
- }
-
- return node
- }
-
- function appendChild(child, node) {
- if (isArrayLike(child)) {
- appendChildren(child, node);
- } else if (isString(child) || isNumber(child)) {
- appendChildToNode(document.createTextNode(child), node);
- } else if (child === null) {
- appendChildToNode(document.createComment(""), node);
- } else if (isElement(child)) {
- appendChildToNode(child, node);
- }
- }
-
- function appendChildren(children, node) {
- for (const child of children) {
- appendChild(child, node);
- }
-
- return node
- }
-
- function appendChildToNode(child, node) {
- if (node instanceof window.HTMLTemplateElement) {
- node.content.appendChild(child);
- } else {
- node.appendChild(child);
- }
- }
-
- function normalizeAttribute(s) {
- return s.replace(/[A-Z\d]/g, (match) => ":" + match.toLowerCase())
- }
-
- function attribute(key, value, node) {
- switch (key) {
- case "xlinkActuate":
- case "xlinkArcrole":
- case "xlinkHref":
- case "xlinkRole":
- case "xlinkShow":
- case "xlinkTitle":
- case "xlinkType":
- attrNS(node, XLinkNamespace, normalizeAttribute(key), value);
- return
-
- case "xmlnsXlink":
- attr(node, normalizeAttribute(key), value);
- return
-
- case "xmlBase":
- case "xmlLang":
- case "xmlSpace":
- attrNS(node, XMLNamespace, normalizeAttribute(key), value);
- return
- }
-
- switch (key) {
- case "htmlFor":
- attr(node, "for", value);
- return
-
- case "dataset":
- forEach(value, (dataValue, dataKey) => {
- if (dataValue != null) {
- node.dataset[dataKey] = dataValue;
- }
- });
- return
-
- case "innerHTML":
- case "innerText":
- case "textContent":
- node[key] = value;
- return
-
- case "spellCheck":
- node.spellcheck = value;
- return
-
- case "class":
- case "className":
- if (isFunction(value)) {
- value(node);
- } else {
- attr(node, "class", className(value));
- }
-
- return
-
- case "ref":
- case "namespaceURI":
- return
-
- case "style":
- if (isObject(value)) {
- forEach(value, (val, key) => {
- if (isNumber(val) && isUnitlessNumber[key] !== 0) {
- node.style[key] = val + "px";
- } else {
- node.style[key] = val;
- }
- });
- return
- }
- }
-
- if (isFunction(value)) {
- if (key[0] === "o" && key[1] === "n") {
- node[key.toLowerCase()] = value;
- }
- } else if (value === true) {
- attr(node, key, "");
- } else if (value !== false && value != null) {
- attr(node, key, value);
- }
- }
-
- function attr(node, key, value) {
- node.setAttribute(key, value);
- }
-
- function attrNS(node, namespace, key, value) {
- node.setAttributeNS(namespace, key, value);
- }
-
- function attributes(attr, node) {
- for (const key of keys(attr)) {
- attribute(key, attr[key], node);
- }
-
- return node
- }
-
- function messageGetter({
- getMessage,
- DEFAULT
- }) {
- return (key, params) => {
- const message = getMessage(key, params);
- if (message) return message;
- const defaultMessage = DEFAULT[key];
- if (!defaultMessage) return "";
- if (!params) return defaultMessage;
-
- if (!Array.isArray(params)) {
- params = [params];
- }
-
- return defaultMessage.replace(/\$(\d+)/g, (m, n) => params[n - 1]);
- };
- }
-
- function fallback(getMessage) {
- return messageGetter({
- getMessage,
- DEFAULT: {
- currentScopeLabel: "Current scope",
- addScopeLabel: "Add new scope",
- deleteScopeLabel: "Delete current scope",
- learnMoreButton: "Learn more",
- importButton: "Import",
- exportButton: "Export",
- addScopePrompt: "Add new scope",
- deleteScopeConfirm: "Delete scope $1?",
- importPrompt: "Paste settings",
- exportPrompt: "Copy settings"
- }
- });
- }
-
- const VALID_CONTROL = new Set(["import", "export", "scope-list", "add-scope", "delete-scope"]);
-
- class DefaultMap extends Map {
- constructor(getDefault) {
- super();
- this.getDefault = getDefault;
- }
-
- get(key) {
- let item = super.get(key);
-
- if (!item) {
- item = this.getDefault();
- super.set(key, item);
- }
-
- return item;
- }
-
- }
-
- function bindInputs(pref, inputs) {
- const bounds = [];
-
- const onPrefChange = change => {
- for (const key in change) {
- if (!inputs.has(key)) {
- continue;
- }
-
- for (const input of inputs.get(key)) {
- updateInput(input, change[key]);
- }
- }
- };
-
- pref.on("change", onPrefChange);
- bounds.push(() => pref.off("change", onPrefChange));
-
- for (const [key, list] of inputs.entries()) {
- for (const input of list) {
- const evt = input.hasAttribute("realtime") ? "input" : "change";
-
- const onChange = () => updatePref(key, input);
-
- input.addEventListener(evt, onChange);
- bounds.push(() => input.removeEventListener(evt, onChange));
- }
- }
-
- onPrefChange(pref.getAll());
- return () => {
- for (const unbind of bounds) {
- unbind();
- }
- };
-
- function updatePref(key, input) {
- if (!input.checkValidity()) {
- return;
- }
-
- if (input.type === "checkbox") {
- pref.set(key, input.checked);
- return;
- }
-
- if (input.type === "radio") {
- if (input.checked) {
- pref.set(key, input.value);
- }
-
- return;
- }
-
- if (input.nodeName === "SELECT" && input.multiple) {
- pref.set(key, [...input.options].filter(o => o.selected).map(o => o.value));
- return;
- }
-
- if (input.type === "number" || input.type === "range") {
- pref.set(key, Number(input.value));
- return;
- }
-
- pref.set(key, input.value);
- }
-
- function updateInput(input, value) {
- if (input.nodeName === "INPUT" && input.type === "radio") {
- input.checked = input.value === value;
- return;
- }
-
- if (input.type === "checkbox") {
- input.checked = value;
- return;
- }
-
- if (input.nodeName === "SELECT" && input.multiple) {
- const checked = new Set(value);
-
- for (const option of input.options) {
- option.selected = checked.has(option.value);
- }
-
- return;
- }
-
- input.value = value;
- }
- }
-
- function bindFields(pref, fields) {
- const onPrefChange = change => {
- for (const key in change) {
- if (!fields.has(key)) {
- continue;
- }
-
- for (const field of fields.get(key)) {
- field.disabled = field.dataset.bindToValue ? field.dataset.bindToValue !== change[key] : !change[key];
- }
- }
- };
-
- pref.on("change", onPrefChange);
- onPrefChange(pref.getAll());
- return () => pref.off("change", onPrefChange);
- }
-
- function bindControls({
- pref,
- controls,
- alert: _alert = alert,
- confirm: _confirm = confirm,
- prompt: _prompt = prompt,
- getMessage = () => {},
- getNewScope = () => ""
- }) {
- const CONTROL_METHODS = {
- "import": ["click", doImport],
- "export": ["click", doExport],
- "scope-list": ["change", updateCurrentScope],
- "add-scope": ["click", addScope],
- "delete-scope": ["click", deleteScope]
- };
-
- for (const type in CONTROL_METHODS) {
- for (const el of controls.get(type)) {
- el.addEventListener(CONTROL_METHODS[type][0], CONTROL_METHODS[type][1]);
- }
- }
-
- pref.on("scopeChange", updateCurrentScopeEl);
- pref.on("scopeListChange", updateScopeList);
- updateScopeList();
- updateCurrentScopeEl();
-
- const _ = fallback(getMessage);
-
- return unbind;
-
- function unbind() {
- pref.off("scopeChange", updateCurrentScopeEl);
- pref.off("scopeListChange", updateScopeList);
-
- for (const type in CONTROL_METHODS) {
- for (const el of controls.get(type)) {
- el.removeEventListener(CONTROL_METHODS[type][0], CONTROL_METHODS[type][1]);
- }
- }
- }
-
- async function doImport() {
- try {
- const input = await _prompt(_("importPrompt"));
-
- if (input == null) {
- return;
- }
-
- const settings = JSON.parse(input);
- return pref.import(settings);
- } catch (err) {
- await _alert(err.message);
- }
- }
-
- async function doExport() {
- try {
- const settings = await pref.export();
- await _prompt(_("exportPrompt"), JSON.stringify(settings));
- } catch (err) {
- await _alert(err.message);
- }
- }
-
- function updateCurrentScope(e) {
- pref.setCurrentScope(e.target.value);
- }
-
- async function addScope() {
- try {
- let scopeName = await _prompt(_("addScopePrompt"), getNewScope());
-
- if (scopeName == null) {
- return;
- }
-
- scopeName = scopeName.trim();
-
- if (!scopeName) {
- throw new Error("the value is empty");
- }
-
- await pref.addScope(scopeName);
- pref.setCurrentScope(scopeName);
- } catch (err) {
- await _alert(err.message);
- }
- }
-
- async function deleteScope() {
- try {
- const scopeName = pref.getCurrentScope();
- const result = await _confirm(_("deleteScopeConfirm", scopeName));
-
- if (result) {
- return pref.deleteScope(scopeName);
- }
- } catch (err) {
- await _alert(err.message);
- }
- }
-
- function updateCurrentScopeEl() {
- const scopeName = pref.getCurrentScope();
-
- for (const el of controls.get("scope-list")) {
- el.value = scopeName;
- }
- }
-
- function updateScopeList() {
- const scopeList = pref.getScopeList();
-
- for (const el of controls.get("scope-list")) {
- el.innerHTML = "";
- el.append(...scopeList.map(scope => {
- const option = document.createElement("option");
- option.value = scope;
- option.textContent = scope;
- return option;
- }));
- }
- }
- }
-
- function createBinding({
- pref,
- root,
- elements = root.querySelectorAll("input, textarea, select, fieldset, button"),
- keyPrefix = "pref-",
- controlPrefix = "webext-pref-",
- alert,
- confirm,
- prompt,
- getMessage,
- getNewScope
- }) {
- const inputs = new DefaultMap(() => []);
- const fields = new DefaultMap(() => []);
- const controls = new DefaultMap(() => []);
-
- for (const element of elements) {
- const id = element.id && stripPrefix(element.id, keyPrefix);
-
- if (id && pref.has(id)) {
- inputs.get(id).push(element);
- continue;
- }
-
- if (element.nodeName === "INPUT" && element.type === "radio") {
- const name = element.name && stripPrefix(element.name, keyPrefix);
-
- if (name && pref.has(name)) {
- inputs.get(name).push(element);
- continue;
- }
- }
-
- if (element.nodeName === "FIELDSET" && element.dataset.bindTo) {
- fields.get(element.dataset.bindTo).push(element);
- continue;
- }
-
- const controlType = findControlType(element.classList);
-
- if (controlType) {
- controls.get(controlType).push(element);
- }
- }
-
- const bounds = [bindInputs(pref, inputs), bindFields(pref, fields), bindControls({
- pref,
- controls,
- alert,
- confirm,
- prompt,
- getMessage,
- getNewScope
- })];
- return () => {
- for (const unbind of bounds) {
- unbind();
- }
- };
-
- function stripPrefix(id, prefix) {
- if (!prefix) {
- return id;
- }
-
- return id.startsWith(prefix) ? id.slice(prefix.length) : "";
- }
-
- function findControlType(list) {
- for (const name of list) {
- const controlType = stripPrefix(name, controlPrefix);
-
- if (VALID_CONTROL.has(controlType)) {
- return controlType;
- }
- }
- }
- }
-
- function createUI({
- body,
- getMessage = () => {},
- toolbar = true,
- navbar = true,
- keyPrefix = "pref-",
- controlPrefix = "webext-pref-"
- }) {
- const root = document.createDocumentFragment();
-
- const _ = fallback(getMessage);
-
- if (toolbar) {
- root.append(createToolbar());
- }
-
- if (navbar) {
- root.append(createNavbar());
- }
-
- root.append( /*#__PURE__*/createElement("div", {
- class: controlPrefix + "body"
- }, body.map(item => {
- if (!item.hLevel) {
- item.hLevel = 3;
- }
-
- return createItem(item);
- })));
- return root;
-
- function createToolbar() {
- return /*#__PURE__*/createElement("div", {
- class: controlPrefix + "toolbar"
- }, /*#__PURE__*/createElement("button", {
- type: "button",
- class: [controlPrefix + "import", "browser-style"]
- }, _("importButton")), /*#__PURE__*/createElement("button", {
- type: "button",
- class: [controlPrefix + "export", "browser-style"]
- }, _("exportButton")));
- }
-
- function createNavbar() {
- return /*#__PURE__*/createElement("div", {
- class: controlPrefix + "nav"
- }, /*#__PURE__*/createElement("select", {
- class: [controlPrefix + "scope-list", "browser-style"],
- title: _("currentScopeLabel")
- }), /*#__PURE__*/createElement("button", {
- type: "button",
- class: [controlPrefix + "delete-scope", "browser-style"],
- title: _("deleteScopeLabel")
- }, "\xD7"), /*#__PURE__*/createElement("button", {
- type: "button",
- class: [controlPrefix + "add-scope", "browser-style"],
- title: _("addScopeLabel")
- }, "+"));
- }
-
- function createItem(p) {
- if (p.type === "section") {
- return createSection(p);
- }
-
- if (p.type === "checkbox") {
- return createCheckbox(p);
- }
-
- if (p.type === "radiogroup") {
- return createRadioGroup(p);
- }
-
- return createInput(p);
- }
-
- function createInput(p) {
- const key = keyPrefix + p.key;
- let input;
- const onChange = p.validate ? e => {
- try {
- p.validate(e.target.value);
- e.target.setCustomValidity("");
- } catch (err) {
- e.target.setCustomValidity(err.message || String(err));
- }
- } : null;
-
- if (p.type === "select") {
- input = /*#__PURE__*/createElement("select", {
- multiple: p.multiple,
- class: "browser-style",
- id: key,
- onChange: onChange
- }, Object.entries(p.options).map(([value, label]) => /*#__PURE__*/createElement("option", {
- value: value
- }, label)));
- } else if (p.type === "textarea") {
- input = /*#__PURE__*/createElement("textarea", {
- rows: "8",
- class: "browser-style",
- id: key,
- onChange: onChange
- });
- } else {
- input = /*#__PURE__*/createElement("input", {
- type: p.type,
- id: key,
- onChange: onChange
- });
- }
-
- return /*#__PURE__*/createElement("div", {
- class: [`${controlPrefix}${p.type}`, "browser-style", p.className]
- }, /*#__PURE__*/createElement("label", {
- htmlFor: key
- }, p.label), p.learnMore && /*#__PURE__*/createElement(LearnMore, {
- url: p.learnMore
- }), input, p.help && /*#__PURE__*/createElement(Help, {
- content: p.help
- }));
- }
-
- function createRadioGroup(p) {
- return /*#__PURE__*/createElement("div", {
- class: [`${controlPrefix}${p.type}`, "browser-style", p.className]
- }, /*#__PURE__*/createElement("div", {
- class: controlPrefix + "radio-title"
- }, p.label), p.learnMore && /*#__PURE__*/createElement(LearnMore, {
- url: p.learnMore
- }), p.help && /*#__PURE__*/createElement(Help, {
- content: p.help
- }), p.children.map(c => {
- c.parentKey = p.key;
- return createCheckbox(inheritProp(p, c));
- }));
- }
-
- function Help({
- content
- }) {
- return /*#__PURE__*/createElement("p", {
- class: controlPrefix + "help"
- }, content);
- }
-
- function LearnMore({
- url
- }) {
- return /*#__PURE__*/createElement("a", {
- href: url,
- class: controlPrefix + "learn-more",
- target: "_blank",
- rel: "noopener noreferrer"
- }, _("learnMoreButton"));
- }
-
- function createCheckbox(p) {
- const id = p.parentKey ? `${keyPrefix}${p.parentKey}-${p.value}` : keyPrefix + p.key;
- return /*#__PURE__*/createElement("div", {
- class: [`${controlPrefix}${p.type}`, "browser-style", p.className]
- }, /*#__PURE__*/createElement("input", {
- type: p.type,
- id: id,
- name: p.parentKey ? keyPrefix + p.parentKey : null,
- value: p.value
- }), /*#__PURE__*/createElement("label", {
- htmlFor: id
- }, p.label), p.learnMore && /*#__PURE__*/createElement(LearnMore, {
- url: p.learnMore
- }), p.help && /*#__PURE__*/createElement(Help, {
- content: p.help
- }), p.children && /*#__PURE__*/createElement("fieldset", {
- class: controlPrefix + "checkbox-children",
- dataset: {
- bindTo: p.parentKey || p.key,
- bindToValue: p.value
- }
- }, p.children.map(c => createItem(inheritProp(p, c)))));
- }
-
- function createSection(p) {
- const Header = `h${p.hLevel}`;
- p.hLevel++;
- return (
- /*#__PURE__*/
- // FIXME: do we need browser-style for section?
- createElement("div", {
- class: [controlPrefix + p.type, p.className]
- }, /*#__PURE__*/createElement(Header, {
- class: controlPrefix + "header"
- }, p.label), p.help && /*#__PURE__*/createElement(Help, {
- content: p.help
- }), p.children && p.children.map(c => createItem(inheritProp(p, c))))
- );
- }
-
- function inheritProp(parent, child) {
- child.hLevel = parent.hLevel;
- return child;
- }
- }
-
- /* eslint-env greasemonkey */
-
- function createGMStorage() {
- const setValue = typeof GM_setValue === "function" ?
- promisify(GM_setValue) : GM.setValue.bind(GM);
- const getValue = typeof GM_getValue === "function" ?
- promisify(GM_getValue) : GM.getValue.bind(GM);
- const deleteValue = typeof GM_deleteValue === "function" ?
- promisify(GM_deleteValue) : GM.deleteValue.bind(GM);
- const events = new EventLite$1;
-
- if (typeof GM_addValueChangeListener === "function") {
- GM_addValueChangeListener("webext-pref-message", (name, oldValue, newValue) => {
- const changes = JSON.parse(newValue);
- for (const key of Object.keys(changes)) {
- if (typeof changes[key] === "object" && changes[key].$undefined) {
- changes[key] = undefined;
- }
- }
- events.emit("change", changes);
- });
- }
-
- return Object.assign(events, {getMany, setMany, deleteMany});
-
- function getMany(keys) {
- return Promise.all(keys.map(k =>
- getValue(`webext-pref/${k}`)
- .then(value => [k, typeof value === "string" ? JSON.parse(value) : value])
- ))
- .then(entries => {
- const output = {};
- for (const [key, value] of entries) {
- output[key] = value;
- }
- return output;
- });
- }
-
- function setMany(changes) {
- return Promise.all(Object.entries(changes).map(([key, value]) =>
- setValue(`webext-pref/${key}`, JSON.stringify(value))
- ))
- .then(() => {
- if (typeof GM_addValueChangeListener === "function") {
- return setValue("webext-pref-message", JSON.stringify(changes));
- }
- events.emit("change", changes);
- });
- }
-
- function deleteMany(keys) {
- return Promise.all(keys.map(k => deleteValue(`webext-pref/${k}`)))
- .then(() => {
- if (typeof GM_addValueChangeListener === "function") {
- const changes = {};
- for (const key of keys) {
- changes[key] = {
- $undefined: true
- };
- }
- return setValue("webext-pref-message", JSON.stringify(changes));
- }
- const changes = {};
- for (const key of keys) {
- changes[key] = undefined;
- }
- events.emit("change", changes);
- });
- }
-
- function promisify(fn) {
- return (...args) => {
- try {
- return Promise.resolve(fn(...args));
- } catch (err) {
- return Promise.reject(err);
- }
- };
- }
- }
-
- /* eslint-env greasemonkey */
-
- function GM_webextPref({
- default: default_,
- separator,
- css = "",
- ...options
- }) {
- const pref = createPref(default_, separator);
- const initializing = pref.connect(createGMStorage());
- let isOpen = false;
-
- const registerMenu =
- typeof GM_registerMenuCommand === "function" ? GM_registerMenuCommand :
- typeof GM !== "undefined" && GM && GM.registerMenuCommand ? GM.registerMenuCommand.bind(GM) :
- undefined;
-
- if (registerMenu) {
- registerMenu(`${getTitle()} - Configure`, openDialog);
- }
-
- return Object.assign(pref, {
- ready: () => initializing,
- openDialog
- });
-
- function openDialog() {
- if (isOpen) {
- return;
- }
- isOpen = true;
-
- let destroyView;
-
- const modal = document.createElement("div");
- modal.className = "webext-pref-modal";
- modal.onclick = () => {
- modal.classList.remove("webext-pref-modal-open");
- modal.addEventListener("transitionend", () => {
- if (destroyView) {
- destroyView();
- }
- modal.remove();
- isOpen = false;
- });
- };
-
- const style = document.createElement("style");
- style.textContent = "body{overflow:hidden}.webext-pref-modal{position:fixed;top:0;right:0;bottom:0;left:0;background:rgba(0,0,0,.5);overflow:auto;z-index:999999;opacity:0;transition:opacity .2s linear;display:flex}.webext-pref-modal-open{opacity:1}.webext-pref-modal::after,.webext-pref-modal::before{content:\"\";display:block;height:30px;visibility:hidden}.webext-pref-iframe-wrap{margin:auto}.webext-pref-iframe{margin:30px 0;display:inline-block;width:100%;max-width:100%;background:#fff;border-width:0;box-shadow:0 0 30px #000;transform:translateY(-20px);transition:transform .2s linear}.webext-pref-modal-open .webext-pref-iframe{transform:none}" + `
- body {
- padding-right: ${window.innerWidth - document.documentElement.offsetWidth}px;
- }
- `;
-
- const iframe = document.createElement("iframe");
- iframe.className = "webext-pref-iframe";
- iframe.srcdoc = `
- <html>
- <head>
- <style class="dialog-style"></style>
- </head>
- <body>
- <div class="dialog-body"></div>
- </body>
- </html>
- `;
-
- const wrap = document.createElement("div");
- wrap.className = "webext-pref-iframe-wrap";
-
- wrap.append(iframe);
- modal.append(style, wrap);
- document.body.appendChild(modal);
-
- iframe.onload = () => {
- iframe.onload = null;
-
- iframe.contentDocument.querySelector(".dialog-style").textContent = "body{display:inline-block;font-size:16px;font-family:sans-serif;white-space:nowrap;overflow:hidden;margin:0;color:#3d3d3d;line-height:1}input[type=number],input[type=text],select,textarea{display:block;width:100%;box-sizing:border-box;height:2em;font:inherit;padding:0 .3em;border:1px solid #9e9e9e;cursor:pointer}select[multiple],textarea{height:6em}input[type=number]:hover,input[type=text]:hover,select:hover,textarea:hover{border-color:#d5d5d5}input[type=number]:focus,input[type=text]:focus,select:focus,textarea:focus{cursor:auto;border-color:#3a93ee}textarea{line-height:1.5}input[type=checkbox],input[type=radio]{display:inline-block;width:1em;height:1em;font:inherit;margin:0}button{box-sizing:border-box;height:2em;font:inherit;border:1px solid #9e9e9e;cursor:pointer;background:0 0}button:hover{border-color:#d5d5d5}button:focus{border-color:#3a93ee}.dialog-body{margin:2em}.webext-pref-toolbar{display:flex;align-items:center;margin-bottom:1em}.dialog-title{font-size:1.34em;margin:0 2em 0 0;flex-grow:1}.webext-pref-toolbar button{font-size:.7em;margin-left:.5em}.webext-pref-nav{display:flex;margin-bottom:1em}.webext-pref-nav select{text-align:center;text-align-last:center}.webext-pref-nav button{width:2em}.webext-pref-number,.webext-pref-radiogroup,.webext-pref-select,.webext-pref-text,.webext-pref-textarea{margin:1em 0}.webext-pref-body>:first-child{margin-top:0}.webext-pref-body>:last-child{margin-bottom:0}.webext-pref-number>input,.webext-pref-select>select,.webext-pref-text>input,.webext-pref-textarea>textarea{margin:.3em 0}.webext-pref-checkbox,.webext-pref-radio{margin:.5em 0;padding-left:1.5em}.webext-pref-checkbox>input,.webext-pref-radio>input{margin-left:-1.5em;margin-right:.5em;vertical-align:middle}.webext-pref-checkbox>label,.webext-pref-radio>label{cursor:pointer;vertical-align:middle}.webext-pref-checkbox>label:hover,.webext-pref-radio>label:hover{color:#707070}.webext-pref-checkbox-children,.webext-pref-radio-children{margin:.7em 0 0;padding:0;border-width:0}.webext-pref-checkbox-children[disabled],.webext-pref-radio-children[disabled]{opacity:.5}.webext-pref-checkbox-children>:first-child,.webext-pref-radio-children>:first-child{margin-top:0}.webext-pref-checkbox-children>:last-child,.webext-pref-radio-children>:last-child{margin-bottom:0}.webext-pref-checkbox-children>:last-child>:last-child,.webext-pref-radio-children>:last-child>:last-child{margin-bottom:0}.webext-pref-help{color:#969696}.responsive{white-space:normal}.responsive .dialog-body{margin:1em}.responsive .webext-pref-toolbar{display:block}.responsive .dialog-title{margin:0 0 1em 0}.responsive .webext-pref-toolbar button{font-size:1em}.responsive .webext-pref-nav{display:block}" + css;
-
- const root = iframe.contentDocument.querySelector(".dialog-body");
- root.append(createUI(options));
-
- destroyView = createBinding({
- pref,
- root,
- ...options
- });
-
- const title = document.createElement("h2");
- title.className = "dialog-title";
- title.textContent = getTitle();
- iframe.contentDocument.querySelector(".webext-pref-toolbar").prepend(title);
-
- if (iframe.contentDocument.body.offsetWidth > modal.offsetWidth) {
- iframe.contentDocument.body.classList.add("responsive");
- }
-
- // calc iframe size
- iframe.style = `
- width: ${iframe.contentDocument.body.offsetWidth}px;
- height: ${iframe.contentDocument.body.scrollHeight}px;
- `;
-
- modal.classList.add("webext-pref-modal-open");
- };
- }
-
- function getTitle() {
- return typeof GM_info === "object" ?
- GM_info.script.name : GM.info.script.name;
- }
- }
-
- return GM_webextPref;
-
- }());