420733b93a
* add Patch CSP option * show style version, size, and update age in manager * add scope selector to style search in manager * keep scroll position and selections in tab's session * directly install usercss from raw github links * ditch localStorage, use on-demand SessionStore proxy * simplify localization * allow <code> tag in i18n-html * keep nodes in HTML templates * API.getAllStyles is actually faster with code untouched * fix fitToContent when applies-to is taller than window * dedupe linter.enableForEditor calls * prioritize visible CMs in refreshOnViewListener * don't scroll to last style on editing a new one * delay colorview for invisible CMs * eslint comma-dangle error + autofix files * styleViaXhr: also toggle for disableAll pref * styleViaXhr: allow cookies for sandbox CSP * simplify notes in options * simplify getStylesViaXhr * oldUI fixups: * remove separator before 1st applies-to * center name bubbles * fix updateToc focus on a newly added section * fix fitToContent when cloning section * remove CSS `contain` as it makes no difference * replace overrides with declarative CSS + code cosmetics * simplify adjustWidth and make it work in FF
177 lines
5.5 KiB
JavaScript
177 lines
5.5 KiB
JavaScript
/* global workerUtil importScripts parseMozFormat metaParser styleCodeEmpty colorConverter */
|
|
'use strict';
|
|
|
|
importScripts('/js/worker-util.js');
|
|
const {loadScript} = workerUtil;
|
|
|
|
workerUtil.createAPI({
|
|
parseMozFormat(arg) {
|
|
loadScript('/vendor-overwrites/csslint/parserlib.js', '/js/moz-parser.js');
|
|
return parseMozFormat(arg);
|
|
},
|
|
compileUsercss,
|
|
parseUsercssMeta(text, indexOffset = 0) {
|
|
loadScript(
|
|
'/vendor/usercss-meta/usercss-meta.min.js',
|
|
'/vendor-overwrites/colorpicker/colorconverter.js',
|
|
'/js/meta-parser.js'
|
|
);
|
|
return metaParser.parse(text, indexOffset);
|
|
},
|
|
nullifyInvalidVars(vars) {
|
|
loadScript(
|
|
'/vendor/usercss-meta/usercss-meta.min.js',
|
|
'/vendor-overwrites/colorpicker/colorconverter.js',
|
|
'/js/meta-parser.js'
|
|
);
|
|
return metaParser.nullifyInvalidVars(vars);
|
|
},
|
|
});
|
|
|
|
function compileUsercss(preprocessor, code, vars) {
|
|
loadScript(
|
|
'/vendor-overwrites/csslint/parserlib.js',
|
|
'/vendor-overwrites/colorpicker/colorconverter.js',
|
|
'/js/moz-parser.js'
|
|
);
|
|
const builder = getUsercssCompiler(preprocessor);
|
|
vars = simpleVars(vars);
|
|
return Promise.resolve(builder.preprocess ? builder.preprocess(code, vars) : code)
|
|
.then(code => parseMozFormat({code}))
|
|
.then(({sections, errors}) => {
|
|
if (builder.postprocess) {
|
|
builder.postprocess(sections, vars);
|
|
}
|
|
return {sections, errors};
|
|
});
|
|
|
|
function simpleVars(vars) {
|
|
if (!vars) {
|
|
return {};
|
|
}
|
|
// simplify vars by merging `va.default` to `va.value`, so BUILDER don't
|
|
// need to test each va's default value.
|
|
return Object.keys(vars).reduce((output, key) => {
|
|
const va = vars[key];
|
|
output[key] = Object.assign({}, va, {
|
|
value: va.value === null || va.value === undefined ?
|
|
getVarValue(va, 'default') : getVarValue(va, 'value'),
|
|
});
|
|
return output;
|
|
}, {});
|
|
}
|
|
|
|
function getVarValue(va, prop) {
|
|
if (va.type === 'select' || va.type === 'dropdown' || va.type === 'image') {
|
|
// TODO: handle customized image
|
|
return va.options.find(o => o.name === va[prop]).value;
|
|
}
|
|
if ((va.type === 'number' || va.type === 'range') && va.units) {
|
|
return va[prop] + va.units;
|
|
}
|
|
return va[prop];
|
|
}
|
|
}
|
|
|
|
function getUsercssCompiler(preprocessor) {
|
|
const BUILDER = {
|
|
default: {
|
|
postprocess(sections, vars) {
|
|
loadScript('/js/sections-util.js');
|
|
let varDef = Object.keys(vars).map(k => ` --${k}: ${vars[k].value};\n`).join('');
|
|
if (!varDef) return;
|
|
varDef = ':root {\n' + varDef + '}\n';
|
|
for (const section of sections) {
|
|
if (!styleCodeEmpty(section.code)) {
|
|
section.code = varDef + section.code;
|
|
}
|
|
}
|
|
},
|
|
},
|
|
stylus: {
|
|
preprocess(source, vars) {
|
|
loadScript('/vendor/stylus-lang-bundle/stylus-renderer.min.js');
|
|
return new Promise((resolve, reject) => {
|
|
const varDef = Object.keys(vars).map(key => `${key} = ${vars[key].value};\n`).join('');
|
|
new self.StylusRenderer(varDef + source)
|
|
.render((err, output) => err ? reject(err) : resolve(output));
|
|
});
|
|
},
|
|
},
|
|
less: {
|
|
preprocess(source, vars) {
|
|
if (!self.less) {
|
|
self.less = {
|
|
logLevel: 0,
|
|
useFileCache: false,
|
|
};
|
|
}
|
|
loadScript('/vendor/less-bundle/less.min.js');
|
|
const varDefs = Object.keys(vars).map(key => `@${key}:${vars[key].value};\n`).join('');
|
|
return self.less.render(varDefs + source)
|
|
.then(({css}) => css);
|
|
},
|
|
},
|
|
uso: {
|
|
preprocess(source, vars) {
|
|
loadScript('/vendor-overwrites/colorpicker/colorconverter.js');
|
|
const pool = new Map();
|
|
return Promise.resolve(doReplace(source));
|
|
|
|
function getValue(name, rgbName) {
|
|
if (!vars.hasOwnProperty(name)) {
|
|
if (name.endsWith('-rgb')) {
|
|
return getValue(name.slice(0, -4), name);
|
|
}
|
|
return null;
|
|
}
|
|
const {type, value} = vars[name];
|
|
switch (type) {
|
|
case 'color': {
|
|
let color = pool.get(rgbName || name);
|
|
if (color == null) {
|
|
color = colorConverter.parse(value);
|
|
if (color) {
|
|
if (color.type === 'hsl') {
|
|
color = colorConverter.HSVtoRGB(colorConverter.HSLtoHSV(color));
|
|
}
|
|
const {r, g, b} = color;
|
|
color = rgbName
|
|
? `${r}, ${g}, ${b}`
|
|
: `#${(0x1000000 + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
|
|
}
|
|
// the pool stores `false` for bad colors to differentiate from a yet unknown color
|
|
pool.set(rgbName || name, color || false);
|
|
}
|
|
return color || null;
|
|
}
|
|
case 'dropdown':
|
|
case 'select': // prevent infinite recursion
|
|
pool.set(name, '');
|
|
return doReplace(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);
|
|
});
|
|
}
|
|
},
|
|
},
|
|
};
|
|
|
|
if (preprocessor) {
|
|
if (!BUILDER[preprocessor]) {
|
|
throw new Error('unknwon preprocessor');
|
|
}
|
|
return BUILDER[preprocessor];
|
|
}
|
|
return BUILDER.default;
|
|
}
|