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) {
|
function getMetaSource(source) {
|
||||||
const commentRe = /\/\*[\s\S]*?\*\//g;
|
const commentRe = /\/\*[\s\S]*?\*\//g;
|
||||||
const metaRe = /==userstyle==[\s\S]*?==\/userstyle==/i;
|
const metaRe = /==userstyle==[\s\S]*?==\/userstyle==/i;
|
||||||
|
@ -219,5 +262,5 @@ var usercss = (function () {
|
||||||
// FIXME: validate variable formats
|
// FIXME: validate variable formats
|
||||||
}
|
}
|
||||||
|
|
||||||
return {buildMeta, buildCode};
|
return {buildMeta, buildCode, colorParser};
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -139,6 +139,8 @@
|
||||||
<script src="manage/filters.js"></script>
|
<script src="manage/filters.js"></script>
|
||||||
<script src="manage/updater-ui.js"></script>
|
<script src="manage/updater-ui.js"></script>
|
||||||
<script src="manage/object-diff.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>
|
<script src="manage/manage.js"></script>
|
||||||
</head>
|
</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 filtersSelector, filterAndAppend */
|
||||||
/* global checkUpdate, handleUpdateInstalled */
|
/* global checkUpdate, handleUpdateInstalled */
|
||||||
/* global objectDiff */
|
/* global objectDiff */
|
||||||
|
/* global configDialog */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
let installed;
|
let installed;
|
||||||
|
@ -282,128 +283,20 @@ Object.assign(handleEvent, {
|
||||||
},
|
},
|
||||||
|
|
||||||
config(event, {styleMeta: style}) {
|
config(event, {styleMeta: style}) {
|
||||||
const form = buildConfigForm();
|
configDialog(style).then(vars => {
|
||||||
|
if (!vars) {
|
||||||
messageBox({
|
return;
|
||||||
title: `Configure ${style.name}`,
|
}
|
||||||
className: 'config-dialog',
|
const keys = Object.keys(vars).filter(k => vars[k].dirty);
|
||||||
contents: form.el,
|
if (!keys.length) {
|
||||||
buttons: [
|
|
||||||
t('confirmSave'),
|
|
||||||
{
|
|
||||||
textContent: t('confirmDefault'),
|
|
||||||
onclick: form.useDefault
|
|
||||||
},
|
|
||||||
t('confirmCancel')
|
|
||||||
]
|
|
||||||
}).then(result => {
|
|
||||||
if (result.button !== 0 && !result.enter) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
style.reason = 'config';
|
style.reason = 'config';
|
||||||
const vars = form.getVars();
|
for (const key of keys) {
|
||||||
let dirty = false;
|
style.vars[key].value = vars[key].value;
|
||||||
for (const key of Object.keys(vars)) {
|
|
||||||
if (vars[key].dirty) {
|
|
||||||
dirty = true;
|
|
||||||
style.vars[key].value = vars[key].value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!dirty) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
saveStyleSafe(style);
|
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) {
|
entryClicked(event) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user