Add: colorParser
This commit is contained in:
parent
1f44898475
commit
4e0f4b34bb
|
@ -46,6 +46,49 @@ var usercss = (function () {
|
|||
}
|
||||
};
|
||||
|
||||
const colorParser = (function () {
|
||||
const el = document.createElement('div');
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=14563
|
||||
document.head.appendChild(el);
|
||||
|
||||
function _parse(color) {
|
||||
const [r, g, b, a = 1] = color.match(/[.\d]+/g).map(Number);
|
||||
return {r, g, b, a};
|
||||
}
|
||||
|
||||
function parse(color) {
|
||||
el.style.color = color;
|
||||
if (el.style.color === '') {
|
||||
throw new Error(`"${color}" is not a valid color`);
|
||||
}
|
||||
color = getComputedStyle(el).color;
|
||||
el.style.color = '';
|
||||
return _parse(color);
|
||||
}
|
||||
|
||||
function format({r, g, b, a = 1}) {
|
||||
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
||||
}
|
||||
|
||||
function pad(s) {
|
||||
if (s.padStart) {
|
||||
// chrome 57+
|
||||
return s.padStart(2, '0');
|
||||
}
|
||||
return `00${s}`.slice(-2);
|
||||
}
|
||||
|
||||
function formatHex({r, g, b, a = null}) {
|
||||
const values = [r, g, b];
|
||||
if (a !== null) {
|
||||
values.push(Math.floor(a * 255));
|
||||
}
|
||||
return '#' + values.map(n => pad(n.toString(16))).join('');
|
||||
}
|
||||
|
||||
return {parse, format, formatHex};
|
||||
})();
|
||||
|
||||
function getMetaSource(source) {
|
||||
const commentRe = /\/\*[\s\S]*?\*\//g;
|
||||
const metaRe = /==userstyle==[\s\S]*?==\/userstyle==/i;
|
||||
|
@ -219,5 +262,5 @@ var usercss = (function () {
|
|||
// FIXME: validate variable formats
|
||||
}
|
||||
|
||||
return {buildMeta, buildCode};
|
||||
return {buildMeta, buildCode, colorParser};
|
||||
})();
|
||||
|
|
|
@ -139,6 +139,8 @@
|
|||
<script src="manage/filters.js"></script>
|
||||
<script src="manage/updater-ui.js"></script>
|
||||
<script src="manage/object-diff.js"></script>
|
||||
<script src="js/usercss.js"></script>
|
||||
<script src="manage/config-dialog.js"></script>
|
||||
<script src="manage/manage.js"></script>
|
||||
</head>
|
||||
|
||||
|
|
121
manage/config-dialog.js
Normal file
121
manage/config-dialog.js
Normal file
|
@ -0,0 +1,121 @@
|
|||
/* global usercss messageBox */
|
||||
|
||||
'use strict';
|
||||
|
||||
function configDialog(style) {
|
||||
const {colorParser} = usercss;
|
||||
const form = buildConfigForm();
|
||||
|
||||
return messageBox({
|
||||
title: `Configure ${style.name}`,
|
||||
className: 'config-dialog',
|
||||
contents: form.el,
|
||||
buttons: [
|
||||
t('confirmSave'),
|
||||
{
|
||||
textContent: t('confirmDefault'),
|
||||
onclick: form.useDefault
|
||||
},
|
||||
t('confirmCancel')
|
||||
]
|
||||
}).then(result => {
|
||||
if (result.button !== 0 && !result.enter) {
|
||||
return;
|
||||
}
|
||||
return form.getVars();
|
||||
});
|
||||
|
||||
function buildConfigForm() {
|
||||
const labels = [];
|
||||
const vars = deepCopy(style.vars);
|
||||
for (const key of Object.keys(vars)) {
|
||||
const va = vars[key];
|
||||
let appendChild;
|
||||
if (va.type === 'color') {
|
||||
va.inputColor = $element({tag: 'input', type: 'color'});
|
||||
// FIXME: i18n
|
||||
va.inputAlpha = $element({tag: 'input', type: 'range', min: 0, max: 1, title: 'Opacity', step: 'any'});
|
||||
va.inputColor.onchange = va.inputAlpha.oninput = () => {
|
||||
va.dirty = true;
|
||||
const color = colorParser.parse(va.inputColor.value);
|
||||
color.a = Number(va.inputAlpha.value);
|
||||
va.value = colorParser.format(color);
|
||||
va.inputColor.style.opacity = color.a;
|
||||
};
|
||||
appendChild = [va.label, va.inputColor, va.inputAlpha];
|
||||
} else if (va.type === 'checkbox') {
|
||||
va.input = $element({tag: 'input', type: 'checkbox'});
|
||||
va.input.onchange = () => {
|
||||
va.dirty = true;
|
||||
va.value = String(Number(va.input.checked));
|
||||
};
|
||||
appendChild = [va.input, $element({tag: 'span', appendChild: va.label})];
|
||||
} else if (va.type === 'select') {
|
||||
va.input = $element({
|
||||
tag: 'select',
|
||||
appendChild: Object.keys(va.select).map(key => $element({
|
||||
tag: 'option', value: key, appendChild: va.select[key]
|
||||
}))
|
||||
});
|
||||
va.input.onchange = () => {
|
||||
va.dirty = true;
|
||||
va.value = va.input.value;
|
||||
};
|
||||
appendChild = [va.label, va.input];
|
||||
} else {
|
||||
va.input = $element({tag: 'input', type: 'text'});
|
||||
va.input.oninput = () => {
|
||||
va.dirty = true;
|
||||
va.value = va.input.value;
|
||||
};
|
||||
appendChild = [va.label, va.input];
|
||||
}
|
||||
labels.push($element({
|
||||
tag: 'label',
|
||||
className: `config-${va.type}`,
|
||||
appendChild
|
||||
}));
|
||||
}
|
||||
drawValues();
|
||||
|
||||
function drawValues() {
|
||||
for (const key of Object.keys(vars)) {
|
||||
const va = vars[key];
|
||||
const value = va.value === null || va.value === undefined ?
|
||||
va.default : va.value;
|
||||
|
||||
if (va.type === 'color') {
|
||||
const color = colorParser.parse(value);
|
||||
va.inputAlpha.value = color.a;
|
||||
va.inputColor.style.opacity = color.a;
|
||||
delete color.a;
|
||||
va.inputColor.value = colorParser.formatHex(color);
|
||||
} else if (va.type === 'checkbox') {
|
||||
va.input.checked = Number(value);
|
||||
} else {
|
||||
va.input.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function useDefault() {
|
||||
for (const key of Object.keys(vars)) {
|
||||
const va = vars[key];
|
||||
va.dirty = va.value !== null && va.value !== undefined &&
|
||||
va.value !== va.default;
|
||||
va.value = null;
|
||||
}
|
||||
drawValues();
|
||||
}
|
||||
|
||||
function getVars() {
|
||||
return vars;
|
||||
}
|
||||
|
||||
return {
|
||||
el: labels,
|
||||
useDefault,
|
||||
getVars
|
||||
};
|
||||
}
|
||||
}
|
125
manage/manage.js
125
manage/manage.js
|
@ -2,6 +2,7 @@
|
|||
/* global filtersSelector, filterAndAppend */
|
||||
/* global checkUpdate, handleUpdateInstalled */
|
||||
/* global objectDiff */
|
||||
/* global configDialog */
|
||||
'use strict';
|
||||
|
||||
let installed;
|
||||
|
@ -282,128 +283,20 @@ Object.assign(handleEvent, {
|
|||
},
|
||||
|
||||
config(event, {styleMeta: style}) {
|
||||
const form = buildConfigForm();
|
||||
|
||||
messageBox({
|
||||
title: `Configure ${style.name}`,
|
||||
className: 'config-dialog',
|
||||
contents: form.el,
|
||||
buttons: [
|
||||
t('confirmSave'),
|
||||
{
|
||||
textContent: t('confirmDefault'),
|
||||
onclick: form.useDefault
|
||||
},
|
||||
t('confirmCancel')
|
||||
]
|
||||
}).then(result => {
|
||||
if (result.button !== 0 && !result.enter) {
|
||||
configDialog(style).then(vars => {
|
||||
if (!vars) {
|
||||
return;
|
||||
}
|
||||
const keys = Object.keys(vars).filter(k => vars[k].dirty);
|
||||
if (!keys.length) {
|
||||
return;
|
||||
}
|
||||
style.reason = 'config';
|
||||
const vars = form.getVars();
|
||||
let dirty = false;
|
||||
for (const key of Object.keys(vars)) {
|
||||
if (vars[key].dirty) {
|
||||
dirty = true;
|
||||
style.vars[key].value = vars[key].value;
|
||||
}
|
||||
}
|
||||
if (!dirty) {
|
||||
return;
|
||||
for (const key of keys) {
|
||||
style.vars[key].value = vars[key].value;
|
||||
}
|
||||
saveStyleSafe(style);
|
||||
});
|
||||
|
||||
function buildConfigForm() {
|
||||
const labels = [];
|
||||
const vars = deepCopy(style.vars);
|
||||
for (const key of Object.keys(vars)) {
|
||||
const va = vars[key];
|
||||
let appendChild;
|
||||
if (va.type === 'color') {
|
||||
va.inputColor = $element({tag: 'input', type: 'color'});
|
||||
// FIXME: i18n
|
||||
va.inputAlpha = $element({tag: 'input', type: 'range', min: 0, max: 255, title: 'Opacity'});
|
||||
va.inputColor.onchange = va.inputAlpha.oninput = () => {
|
||||
va.dirty = true;
|
||||
va.value = va.inputColor.value + Number(va.inputAlpha.value).toString(16);
|
||||
va.inputColor.style.opacity = va.inputAlpha.value / 255;
|
||||
};
|
||||
appendChild = [va.label, va.inputColor, va.inputAlpha];
|
||||
} else if (va.type === 'checkbox') {
|
||||
va.input = $element({tag: 'input', type: 'checkbox'});
|
||||
va.input.onchange = () => {
|
||||
va.dirty = true;
|
||||
va.value = String(Number(va.input.checked));
|
||||
};
|
||||
appendChild = [va.input, $element({tag: 'span', appendChild: va.label})];
|
||||
} else if (va.type === 'select') {
|
||||
va.input = $element({
|
||||
tag: 'select',
|
||||
appendChild: Object.keys(va.select).map(key => $element({
|
||||
tag: 'option', value: key, appendChild: va.select[key]
|
||||
}))
|
||||
});
|
||||
va.input.onchange = () => {
|
||||
va.dirty = true;
|
||||
va.value = va.input.value;
|
||||
};
|
||||
appendChild = [va.label, va.input];
|
||||
} else {
|
||||
va.input = $element({tag: 'input', type: 'text'});
|
||||
va.input.oninput = () => {
|
||||
va.dirty = true;
|
||||
va.value = va.input.value;
|
||||
};
|
||||
appendChild = [va.label, va.input];
|
||||
}
|
||||
labels.push($element({
|
||||
tag: 'label',
|
||||
className: `config-${va.type}`,
|
||||
appendChild
|
||||
}));
|
||||
}
|
||||
drawValues();
|
||||
|
||||
function drawValues() {
|
||||
for (const key of Object.keys(vars)) {
|
||||
const va = vars[key];
|
||||
const value = va.value === null || va.value === undefined ?
|
||||
va.default : va.value;
|
||||
|
||||
if (va.type === 'color') {
|
||||
va.inputColor.value = value.slice(0, -2);
|
||||
va.inputAlpha.value = parseInt(value.slice(-2), 16);
|
||||
va.inputColor.style.opacity = va.inputAlpha.value / 255;
|
||||
} else if (va.type === 'checkbox') {
|
||||
va.input.checked = Number(value);
|
||||
} else {
|
||||
va.input.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function useDefault() {
|
||||
for (const key of Object.keys(vars)) {
|
||||
const va = vars[key];
|
||||
va.dirty = va.value !== null && va.value !== undefined &&
|
||||
va.value !== va.default;
|
||||
va.value = null;
|
||||
}
|
||||
drawValues();
|
||||
}
|
||||
|
||||
function getVars() {
|
||||
return vars;
|
||||
}
|
||||
|
||||
return {
|
||||
el: labels,
|
||||
useDefault,
|
||||
getVars
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
entryClicked(event) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user