preserve opacity in colorpicker for preprocessor uso config (#1200)
USO has always produced 6-digit #rrggbb so some styles rely on it and use /*[[color]]*/11 notation to specify the transparency. Now we will try to preserve the opacity customized by the user via colorpicker unless the style specifies it inline.
This commit is contained in:
parent
7d08fea5e1
commit
65ac351699
|
@ -5,6 +5,7 @@
|
||||||
/* global db */
|
/* global db */
|
||||||
/* global prefs */
|
/* global prefs */
|
||||||
/* global tabMan */
|
/* global tabMan */
|
||||||
|
/* global usercssMan */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -202,7 +203,12 @@ const styleMan = (() => {
|
||||||
/** @returns {Promise<StyleObj[]>} */
|
/** @returns {Promise<StyleObj[]>} */
|
||||||
async importMany(items) {
|
async importMany(items) {
|
||||||
if (ready.then) await ready;
|
if (ready.then) await ready;
|
||||||
items.forEach(beforeSave);
|
for (const style of items) {
|
||||||
|
beforeSave(style);
|
||||||
|
if (style.sourceCode && style.usercssData) {
|
||||||
|
await usercssMan.buildCode(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
const events = await db.exec('putMany', items);
|
const events = await db.exec('putMany', items);
|
||||||
return Promise.all(items.map((item, i) => {
|
return Promise.all(items.map((item, i) => {
|
||||||
afterSave(item, events[i]);
|
afterSave(item, events[i]);
|
||||||
|
@ -210,12 +216,6 @@ const styleMan = (() => {
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj>} */
|
|
||||||
async import(data) {
|
|
||||||
if (ready.then) await ready;
|
|
||||||
return handleSave(await saveStyle(data), 'import');
|
|
||||||
},
|
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj>} */
|
/** @returns {Promise<StyleObj>} */
|
||||||
async install(style, reason = null) {
|
async install(style, reason = null) {
|
||||||
if (ready.then) await ready;
|
if (ready.then) await ready;
|
||||||
|
|
|
@ -16,29 +16,27 @@ const colorConverter = (() => {
|
||||||
// NAMED_COLORS is added below
|
// NAMED_COLORS is added below
|
||||||
};
|
};
|
||||||
|
|
||||||
function format(color = '', type = color.type, hexUppercase) {
|
function format(color = '', type = color.type, hexUppercase, usoMode) {
|
||||||
if (!color || !type) return typeof color === 'string' ? color : '';
|
if (!color || !type) return typeof color === 'string' ? color : '';
|
||||||
const a = formatAlpha(color.a);
|
const {a} = color;
|
||||||
const hasA = Boolean(a);
|
let aStr = formatAlpha(a);
|
||||||
if (type === 'rgb' && color.type === 'hsl') {
|
if (aStr) aStr = ', ' + aStr;
|
||||||
|
if (type !== 'hsl' && color.type === 'hsl') {
|
||||||
color = HSVtoRGB(HSLtoHSV(color));
|
color = HSVtoRGB(HSLtoHSV(color));
|
||||||
}
|
}
|
||||||
const {r, g, b, h, s, l} = color;
|
const {r, g, b, h, s, l} = color;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'hex': {
|
case 'hex': {
|
||||||
const rgbStr = (0x1000000 + (r << 16) + (g << 8) + (b | 0)).toString(16).slice(1);
|
let res = '#' + hex2(r) + hex2(g) + hex2(b) + (aStr ? hex2(Math.round(a * 255)) : '');
|
||||||
const aStr = hasA ? (0x100 + Math.round(a * 255)).toString(16).slice(1) : '';
|
if (!usoMode) res = res.replace(/^#(.)\1(.)\2(.)\3(?:(.)\4)?$/, '#$1$2$3$4');
|
||||||
const hexStr = `#${rgbStr + aStr}`.replace(/^#(.)\1(.)\2(.)\3(?:(.)\4)?$/, '#$1$2$3$4');
|
return hexUppercase ? res.toUpperCase() : res;
|
||||||
return hexUppercase ? hexStr.toUpperCase() : hexStr.toLowerCase();
|
}
|
||||||
|
case 'rgb': {
|
||||||
|
const rgb = [r, g, b].map(Math.round).join(', ');
|
||||||
|
return usoMode ? rgb : `rgb${aStr ? 'a' : ''}(${rgb}${aStr})`;
|
||||||
}
|
}
|
||||||
case 'rgb':
|
|
||||||
return hasA ?
|
|
||||||
`rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})` :
|
|
||||||
`rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;
|
|
||||||
case 'hsl':
|
case 'hsl':
|
||||||
return hasA ?
|
return `hsl${aStr ? 'a' : ''}(${h}, ${s}%, ${l}%${aStr})`;
|
||||||
`hsla(${h}, ${s}%, ${l}%, ${a})` :
|
|
||||||
`hsl(${h}, ${s}%, ${l}%)`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +213,10 @@ const colorConverter = (() => {
|
||||||
const int = Math.round(num);
|
const int = Math.round(num);
|
||||||
return Math.abs(int - num) < 1e-3 ? int : num;
|
return Math.abs(int - num) < 1e-3 ? int : num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hex2(val) {
|
||||||
|
return (val < 16 ? '0' : '') + (val >> 0).toString(16);
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
colorConverter.NAMED_COLORS = new Map([
|
colorConverter.NAMED_COLORS = new Map([
|
||||||
|
|
|
@ -47,53 +47,50 @@ const BUILDERS = Object.assign(Object.create(null), {
|
||||||
uso: {
|
uso: {
|
||||||
pre(source, vars) {
|
pre(source, vars) {
|
||||||
require(['/js/color/color-converter']); /* global colorConverter */
|
require(['/js/color/color-converter']); /* global colorConverter */
|
||||||
const pool = new Map();
|
const pool = Object.create(null);
|
||||||
return doReplace(source);
|
return doReplace(source);
|
||||||
|
|
||||||
function getValue(name, rgbName) {
|
function doReplace(text) {
|
||||||
if (!vars.hasOwnProperty(name)) {
|
return text.replace(/(\/\*\[\[([\w-]+)]]\*\/)([0-9a-f]{2}(?=\W))?/gi, (_, cmt, name, alpha) => {
|
||||||
if (name.endsWith('-rgb')) {
|
const key = alpha ? name + '[A]' : name;
|
||||||
return getValue(name.slice(0, -4), name);
|
let val = pool[key];
|
||||||
|
if (val === undefined) {
|
||||||
|
val = pool[key] = getValue(name, null, alpha);
|
||||||
}
|
}
|
||||||
return null;
|
return (val != null ? val : cmt) + (alpha || '');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const {type, value} = vars[name];
|
|
||||||
switch (type) {
|
function getValue(name, isUsoRgb, alpha) {
|
||||||
case 'color': {
|
const v = vars[name];
|
||||||
let color = pool.get(rgbName || name);
|
if (!v) {
|
||||||
if (color == null) {
|
return name.endsWith('-rgb')
|
||||||
color = colorConverter.parse(value);
|
? getValue(name.slice(0, -4), true)
|
||||||
if (color) {
|
: null;
|
||||||
if (color.type === 'hsl') {
|
|
||||||
color = colorConverter.HSVtoRGB(colorConverter.HSLtoHSV(color));
|
|
||||||
}
|
}
|
||||||
const {r, g, b} = color;
|
let {value} = v;
|
||||||
color = rgbName
|
switch (v.type) {
|
||||||
? `${r}, ${g}, ${b}`
|
case 'color':
|
||||||
: `#${(0x1000000 + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
|
value = colorConverter.parse(value) || null;
|
||||||
}
|
if (value) {
|
||||||
// the pool stores `false` for bad colors to differentiate from a yet unknown color
|
/* #rrggbb - inline alpha is present; an opaque hsl/a; #rrggbb originally
|
||||||
pool.set(rgbName || name, color || false);
|
* rgba(r, g, b, a) - transparency <1 is present (Chrome pre-66 compatibility)
|
||||||
}
|
* rgb(r, g, b) - if color is rgb/a with a=1, note: r/g/b will be rounded
|
||||||
return color || null;
|
* r, g, b - if the var has `-rgb` suffix per USO specification
|
||||||
|
* TODO: when minimum_chrome_version >= 66 try to keep `value` intact */
|
||||||
|
if (alpha) delete value.a;
|
||||||
|
const isRgb = isUsoRgb || value.type === 'rgb' || value.a != null && value.a !== 1;
|
||||||
|
const usoMode = isUsoRgb || !isRgb;
|
||||||
|
value = colorConverter.format(value, isRgb ? 'rgb' : 'hex', undefined, usoMode);
|
||||||
}
|
}
|
||||||
|
return value;
|
||||||
case 'dropdown':
|
case 'dropdown':
|
||||||
case 'select': // prevent infinite recursion
|
case 'select':
|
||||||
pool.set(name, '');
|
pool[name] = ''; // prevent infinite recursion
|
||||||
return doReplace(value);
|
return doReplace(value);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function doReplace(text) {
|
|
||||||
return text.replace(/\/\*\[\[([\w-]+)\]\]\*\//g, (match, name) => {
|
|
||||||
if (!pool.has(name)) {
|
|
||||||
const value = getValue(name);
|
|
||||||
pool.set(name, value === null ? match : value);
|
|
||||||
}
|
|
||||||
return pool.get(name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -278,7 +278,7 @@ async function importFromString(jsonString) {
|
||||||
tasks = tasks.then(() => API.styles.delete(id));
|
tasks = tasks.then(() => API.styles.delete(id));
|
||||||
const oldStyle = oldStylesById.get(id);
|
const oldStyle = oldStylesById.get(id);
|
||||||
if (oldStyle) {
|
if (oldStyle) {
|
||||||
tasks = tasks.then(() => API.styles.import(oldStyle));
|
tasks = tasks.then(() => API.styles.importMany([oldStyle]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// taskUI is superfast and updates style list only in this page,
|
// taskUI is superfast and updates style list only in this page,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user