// ==UserScript==
// @name GM_webextPref
// @version 0.3.0
// @description A config library powered by webext-pref.
// @license MIT
// @author eight04 <[email protected]>
// @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
// @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_
});
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 ready() {
return initializing;
}
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;
}
}
/* global chrome */
/* global browser chrome */
function promisify(fn) {
return (...args) => {
try {
return Promise.resolve(fn(...args));
} catch (err) {
return Promise.reject(err);
}
};
}
function createView({
root,
pref,
body,
getMessage = () => {},
getNewScope = () => "",
prompt: _prompt = promisify(prompt),
alert: _alert = promisify(alert),
confirm: _confirm = promisify(confirm)
}) {
getMessage = createGetMessage(getMessage);
const toolbar = createToolbar();
const nav = createNav();
const form = createForm(body);
root.append(toolbar.frag, nav.frag, form.frag);
pref.on("scopeListChange", nav.updateScopeList);
nav.updateScopeList(pref.getScopeList());
pref.on("scopeChange", nav.updateScope);
nav.updateScope(pref.getCurrentScope());
pref.on("change", form.updateInputs);
form.updateInputs(pref.getAll());
return destroy;
function createGetMessage(userGetMessage) {
const DEFAULT = {
currentScopeLabel: "Current scope",
addScopeLabel: "Add new scope",
addScopePrompt: "Add new scope",
deleteScopeLabel: "Delete current scope",
deleteScopeConfirm: scope => `Delete scope ${scope}?`,
learnMoreButton: "Learn more",
importButton: "Import",
exportButton: "Export",
importPrompt: "Paste settings",
exportPrompt: "Copy settings"
};
return (key, params) => {
const message = userGetMessage(key, params);
if (message) {
return message;
}
if (typeof DEFAULT[key] === "function") {
return DEFAULT[key](params);
}
return DEFAULT[key];
};
}
function createToolbar() {
const container = document.createElement("div");
container.className = "webext-pref-toolbar";
const importButton = document.createElement("button");
importButton.className = "webext-pref-import browser-style";
importButton.type = "button";
importButton.textContent = getMessage("importButton");
importButton.onclick = () => {
_prompt(getMessage("importPrompt"))
.then(input => {
if (input == null) {
return;
}
const settings = JSON.parse(input);
return pref.import(settings);
})
.catch(err => _alert(err.message));
};
const exportButton = document.createElement("button");
exportButton.className = "webext-pref-export browser-style";
exportButton.type = "button";
exportButton.textContent = getMessage("exportButton");
exportButton.onclick = () => {
pref.export()
.then(settings =>
_prompt(getMessage("exportPrompt"), JSON.stringify(settings))
)
.catch(err => _alert(err.message));
};
container.append(importButton, exportButton);
return {frag: container};
}
function destroy() {
root.innerHTML = "";
pref.off("scopeChange", nav.updateScope);
pref.off("scopeListChange", nav.updateScopeList);
pref.off("change", form.updateInputs);
}
function createForm(body) {
const container = document.createElement("div");
container.className = "webext-pref-body";
const _body = createBody({body});
container.append(..._body.frag);
return Object.assign(_body, {updateInputs, frag: container});
function updateInputs(changes) {
for (const [key, value] of Object.entries(changes)) {
if (_body.inputs[key]) {
_body.inputs[key].setValue(value);
}
}
}
}
function createNav() {
const container = document.createElement("div");
container.className = "webext-pref-nav";
const select = document.createElement("select");
select.className = "browser-style";
select.title = getMessage("currentScopeLabel");
select.onchange = () => {
pref.setCurrentScope(select.value);
};
const deleteButton = document.createElement("button");
deleteButton.className = "browser-style webext-pref-delete-scope";
deleteButton.title = getMessage("deleteScopeLabel");
deleteButton.type = "button";
deleteButton.textContent = "×";
deleteButton.onclick = () => {
const scopeName = pref.getCurrentScope();
_confirm(getMessage("deleteScopeConfirm", scopeName))
.then(result => {
if (result) {
return pref.deleteScope(scopeName);
}
})
.catch(err => _alert(err.message));
};
const addButton = document.createElement("button");
addButton.className = "browser-style webext-pref-add-scope";
addButton.title = getMessage("addScopeLabel");
addButton.type = "button";
addButton.textContent = "+";
addButton.onclick = () => {
_prompt(getMessage("addScopePrompt"), getNewScope())
.then(scopeName => {
if (scopeName == null) {
return;
}
scopeName = scopeName.trim();
if (!scopeName) {
throw new Error("the value is empty");
}
return pref.addScope(scopeName)
.then(() => pref.setCurrentScope(scopeName));
})
.catch(err => {
_alert(err.message);
});
};
container.append(select, deleteButton, addButton);
return {
updateScope,
updateScopeList,
frag: container
};
function updateScope(newScope) {
select.value = newScope;
}
function updateScopeList(scopeList) {
select.innerHTML = "";
select.append(...scopeList.map(scope => {
const option = document.createElement("option");
option.value = scope;
option.textContent = scope;
return option;
}));
}
}
function createBody({body, hLevel = 3}) {
const inputs = {};
const frag = [];
for (const el of body) {
const container = document.createElement("div");
container.className = `webext-pref-${el.type} browser-style`;
Object.assign(
el,
el.type === "section" ? createSection({el, hLevel}) :
el.type === "checkbox" ? createCheckbox(el) :
el.type === "radiogroup" ? createRadioGroup(el) :
createInput(el)
);
if (el.key) {
inputs[el.key] = el;
}
if (el.inputs) {
Object.assign(inputs, el.inputs);
}
container.append(...el.frag);
frag.push(container);
}
return {
inputs,
frag
};
}
function createSection({el, hLevel}) {
const header = document.createElement(`h${hLevel}`);
header.className = "webext-pref-header";
header.textContent = el.label;
const body = createBody({
body: el.children,
hLevel: hLevel + 1
});
const frag = [header];
if (el.help) {
frag.push(createHelp(el.help));
}
frag.push(...body.frag);
return {
inputs: body.inputs,
frag
};
}
function createCheckbox(el) {
const input = document.createElement("input");
const inputs = {};
input.id = `pref-${el.key}`;
input.type = el.type;
if (el.type === "checkbox") {
// checkbox
input.onchange = () => {
pref.set(el.key, input.checked);
};
} else {
// radio
input.name = `pref-${el.parentKey}`;
input.onchange = () => {
if (input.checked) {
pref.set(el.parentKey, el.value);
}
};
}
const frag = [
input,
createLabel(el.label, input.id)
];
if (el.learnMore) {
frag.push(createLearnMore(el.learnMore));
}
if (el.help) {
frag.push(createHelp(el.help));
}
const childContainer = el.children && el.children.length ?
createChildContainer(el.children) : null;
if (childContainer) {
frag.push(childContainer);
}
return {
inputs,
frag,
setValue: value => {
input.checked = value;
if (childContainer) {
childContainer.disabled = !input.checked;
}
}
};
function createChildContainer(children) {
const container = document.createElement("fieldset");
container.className = "webext-pref-checkbox-children";
container.disabled = true;
const body = createBody({body: children});
container.append(...body.frag);
Object.assign(inputs, body.inputs);
return container;
}
}
function createRadioGroup(el) {
const frag = [];
const inputs = {};
const radios = el.children.map(option => {
const container = document.createElement("div");
container.className = "webext-pref-checkbox browser-style";
const checkbox = createCheckbox(Object.assign({}, option, {
key: `${el.key}-${option.value}`,
parentKey: el.key
}));
Object.assign(inputs, checkbox.inputs);
container.append(...checkbox.frag);
return Object.assign(checkbox, {frag: container, key: option.value});
});
const radioMap = {};
for (const radio of radios) {
radioMap[radio.key] = radio;
}
if (el.label) {
frag.push(createTitle(el.label));
}
if (el.learnMore) {
frag.push(createLearnMore(el.learnMore));
}
if (el.help) {
frag.push(createHelp(el.help));
}
frag.push(...radios.map(r => r.frag));
let currentValue;
return {
inputs,
frag,
setValue: value => {
if (currentValue) {
radioMap[currentValue].setValue(false);
}
radioMap[value].setValue(true);
currentValue = value;
}
};
function createTitle(text) {
const title = document.createElement("span");
title.className = "webext-pref-radio-title";
title.textContent = text;
return title;
}
}
function createInput(el) {
const frag = [];
let input;
if (el.type === "select") {
input = document.createElement("select");
input.className = "browser-style";
input.multiple = el.multiple;
input.append(...Object.entries(el.options).map(([key, value]) => {
const option = document.createElement("option");
option.value = key;
option.textContent = value;
return option;
}));
} else if (el.type === "textarea") {
input = document.createElement("textarea");
input.rows = "8";
input.className = "browser-style";
} else {
input = document.createElement("input");
input.type = el.type;
}
input.id = `pref-${el.key}`;
input.onchange = () => {
const value = el.type === "select" && el.multiple ?
[...input.selectedOptions].map(i => i.value) :
el.type === "number" ? Number(input.value) : input.value;
if (el.validate) {
let err;
try {
el.validate(value);
} catch (_err) {
err = _err;
}
input.setCustomValidity(err ? err.message : "");
if (err) {
return;
}
}
pref.set(el.key, value);
};
frag.push(createLabel(el.label, input.id));
if (el.learnMore) {
frag.push(createLearnMore(el.learnMore));
}
frag.push(input);
if (el.help) {
frag.push(createHelp(el.help));
}
return {
frag,
setValue: value => {
if (el.type !== "select" || !el.multiple) {
input.value = value;
} else {
const set = new Set(value);
for (const option of input.options) {
option.selected = set.has(option.value);
}
}
if (el.validate) {
input.setCustomValidity("");
}
}
};
}
function createHelp(text) {
const help = document.createElement("div");
help.className = "webext-pref-help";
help.textContent = text;
return help;
}
function createLabel(text, id) {
const label = document.createElement("label");
label.textContent = text;
label.htmlFor = id;
return label;
}
function createLearnMore(url) {
const a = document.createElement("a");
a.className = "webext-pref-learn-more";
a.href = url;
a.target = "_blank";
a.rel = "noopener";
a.textContent = getMessage("learnMoreButton");
return a;
}
}
/* 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,
body,
getNewScope,
getMessage,
alert,
confirm,
prompt
}) {
const pref = createPref(_default);
const initializing = pref.connect(createGMStorage());
let isOpen = false;
if (typeof GM_registerMenuCommand === "function") {
GM_registerMenuCommand(`${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 = e => {
if (e.target === modal) {
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;text-align:center}.webext-pref-modal-open{opacity:1}.webext-pref-modal::before{content:\"\";display:inline-block;height:100%;vertical-align:middle}.webext-pref-iframe{display:inline-block;vertical-align:middle;width:90%;background:#fff;margin:30px 0;box-shadow:0 0 30px #000;border-width:0;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>
`;
modal.append(style, iframe);
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-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{margin:.5em 0;padding-left:1.5em}.webext-pref-checkbox>input{margin-left:-1.5em;margin-right:.5em;vertical-align:middle}.webext-pref-checkbox>label{cursor:pointer;vertical-align:middle}.webext-pref-checkbox>label:hover{color:#707070}.webext-pref-checkbox-children{margin:.7em 0 0;padding:0;border-width:0}.webext-pref-checkbox-children[disabled]{opacity:.5}.webext-pref-checkbox-children>:first-child{margin-top:0}.webext-pref-checkbox-children>:last-child{margin-bottom:0}.webext-pref-checkbox-children>:last-child>:last-child{margin-bottom:0}.webext-pref-help{color:#969696}";
destroyView = createView({
pref,
body,
root: iframe.contentDocument.querySelector(".dialog-body"),
getNewScope,
getMessage,
alert,
confirm,
prompt
});
const title = document.createElement("h2");
title.className = "dialog-title";
title.textContent = getTitle();
iframe.contentDocument.querySelector(".webext-pref-toolbar").prepend(title);
// 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;
}());