dece4b57f3
Fix: handle dup name+namespace Fix: eslint eqeqeq Fix: trim @name's spaces Add: check update for userstyle Add: build CSS variable Fix: only check dup when id is not provided Refactor: userStyle2json -> userstyle.json Add: style for input Add: config dialog Fix: preserve config during update Fix: onchange doesn't fire on keyboard enter event Fix: remove empty file Add: validator. Metas must stay in the same line Add: warn the user if installation failed Fix: add some delay before starting installation Add: open the editor after first installation Fix: add openEditor to globals Fix: i18n Add: preprocessor. Move userstyle.build to background page. Fix: remove unused global Fix: preserved unknown prop in saveStyleSource() like saveStyle() Add: edit userstyle source Fix: load preprocessor dynamically Fix: load content script dynamically Fix: buildCode is async function Fix: drop Object.entries Fix: style.sections is undefined Fix: don't hide the name input but disable it Fix: query the style before installation Revert: changes to editor, editor.html Refactor: use term `usercss` instead of `userstyle` Fix: don't show homepage action for usercss Refactor: move script-loader to js/ Refactor: pull out mozParser Fix: code style Fix: we don't need to build meta anymore Fix: use saveUsercss instead of saveStyle to get responsed error Fix: last is undefined, load script error Fix: switch to moz-format Fix: drop injectContentScript. Move usercss check into install-user-css Fix: response -> respond Fix: globals -> global Fix: queryUsercss -> filterUsercss Fix: add processUsercss function Fix: only open editor for usercss Fix: remove findupUsercss fixme Fix: globals -> global Fix: globals -> global Fix: global pollution Revert: update.js Refactor: checkStyle Add: support usercss Fix: no need to getURL in background page Fix: merget semver.js into usercss.js Fix: drop all_urls in match pattern Fix: drop respondWithError Move stylus -> stylus-lang Add stylus-lang/readme Fix: use include_globs Fix: global pollution
203 lines
4.8 KiB
JavaScript
203 lines
4.8 KiB
JavaScript
/* global loadScript mozParser */
|
|
|
|
'use strict';
|
|
|
|
// eslint-disable-next-line no-var
|
|
var usercss = (function () {
|
|
function semverTest(a, b) {
|
|
a = a.split('.').map(Number);
|
|
b = b.split('.').map(Number);
|
|
|
|
for (let i = 0; i < a.length; i++) {
|
|
if (!(i in b)) {
|
|
return 1;
|
|
}
|
|
if (a[i] < b[i]) {
|
|
return -1;
|
|
}
|
|
if (a[i] > b[i]) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (a.length < b.length) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
function guessType(value) {
|
|
if (/^url\(.+\)$/i.test(value)) {
|
|
return 'image';
|
|
}
|
|
if (/^#[0-9a-f]{3,8}$/i.test(value)) {
|
|
return 'color';
|
|
}
|
|
if (/^hsla?\(.+\)$/i.test(value)) {
|
|
return 'color';
|
|
}
|
|
if (/^rgba?\(.+\)$/i.test(value)) {
|
|
return 'color';
|
|
}
|
|
// should we use a color-name table to guess type?
|
|
return 'text';
|
|
}
|
|
|
|
const BUILDER = {
|
|
default: {
|
|
postprocess(sections, vars) {
|
|
let varDef = ':root {\n';
|
|
for (const key of Object.keys(vars)) {
|
|
varDef += ` --${key}: ${vars[key].value};\n`;
|
|
}
|
|
varDef += '}\n';
|
|
|
|
for (const section of sections) {
|
|
section.code = varDef + section.code;
|
|
}
|
|
}
|
|
},
|
|
stylus: {
|
|
preprocess(source, vars) {
|
|
return loadScript('vendor/stylus-lang/stylus.min.js').then(() => (
|
|
new Promise((resolve, reject) => {
|
|
let varDef = '';
|
|
for (const key of Object.keys(vars)) {
|
|
varDef += `${key} = ${vars[key].value};\n`;
|
|
}
|
|
|
|
// eslint-disable-next-line no-undef
|
|
stylus(varDef + source).render((err, output) => {
|
|
if (err) {
|
|
reject(err);
|
|
} else {
|
|
resolve(output);
|
|
}
|
|
});
|
|
})
|
|
));
|
|
}
|
|
}
|
|
};
|
|
|
|
function getMetaSource(source) {
|
|
const commentRe = /\/\*[\s\S]*?\*\//g;
|
|
const metaRe = /==userstyle==[\s\S]*?==\/userstyle==/i;
|
|
|
|
let m;
|
|
// iterate through each comment
|
|
while ((m = commentRe.exec(source))) {
|
|
const commentSource = source.slice(m.index, m.index + m[0].length);
|
|
const n = commentSource.match(metaRe);
|
|
if (n) {
|
|
return n[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
function buildMeta(source) {
|
|
const style = _buildMeta(source);
|
|
validate(style);
|
|
return style;
|
|
}
|
|
|
|
function _buildMeta(source) {
|
|
const style = {
|
|
name: null,
|
|
usercss: true,
|
|
version: null,
|
|
source: source,
|
|
enabled: true,
|
|
sections: [],
|
|
vars: {},
|
|
preprocessor: null
|
|
};
|
|
|
|
const metaSource = getMetaSource(source);
|
|
|
|
const match = (re, callback) => {
|
|
let m;
|
|
if (!re.global) {
|
|
if ((m = metaSource.match(re))) {
|
|
if (m.length === 1) {
|
|
callback(m[0]);
|
|
} else {
|
|
callback(...m.slice(1));
|
|
}
|
|
}
|
|
} else {
|
|
const result = [];
|
|
while ((m = re.exec(metaSource))) {
|
|
if (m.length <= 2) {
|
|
result.push(m[m.length - 1]);
|
|
} else {
|
|
result.push(m.slice(1));
|
|
}
|
|
}
|
|
if (result.length) {
|
|
callback(result);
|
|
}
|
|
}
|
|
};
|
|
|
|
// FIXME: finish all metas
|
|
match(/@name[^\S\r\n]+(.+?)[^\S\r\n]*$/m, m => (style.name = m));
|
|
match(/@namespace[^\S\r\n]+(\S+)/, m => (style.namespace = m));
|
|
match(/@preprocessor[^\S\r\n]+(\S+)/, m => (style.preprocessor = m));
|
|
match(/@version[^\S\r\n]+(\S+)/, m => (style.version = m));
|
|
match(
|
|
/@var[^\S\r\n]+(\S+)[^\S\r\n]+(?:(['"])((?:\\\2|.)*?)\2|(\S+))[^\S\r\n]+(.+?)[^\S\r\n]*$/gm,
|
|
ms => ms.forEach(([key,, label1, label2, value]) => (
|
|
style.vars[key] = {
|
|
type: guessType(value),
|
|
label: label1 || label2,
|
|
value: value
|
|
}
|
|
))
|
|
);
|
|
|
|
return style;
|
|
}
|
|
|
|
function buildCode(style) {
|
|
let builder;
|
|
if (style.preprocessor && style.preprocessor in BUILDER) {
|
|
builder = BUILDER[style.preprocessor];
|
|
} else {
|
|
builder = BUILDER.default;
|
|
}
|
|
|
|
return Promise.resolve().then(() => {
|
|
// preprocess
|
|
if (builder.preprocess) {
|
|
return builder.preprocess(style.source, style.vars);
|
|
}
|
|
return style.source;
|
|
}).then(mozStyle =>
|
|
// moz-parser
|
|
loadScript('/js/moz-parser.js').then(() =>
|
|
mozParser.parse(mozStyle).then(sections => {
|
|
style.sections = sections;
|
|
})
|
|
)
|
|
).then(() => {
|
|
// postprocess
|
|
if (builder.postprocess) {
|
|
return builder.postprocess(style.sections, style.vars);
|
|
}
|
|
}).then(() => style);
|
|
}
|
|
|
|
function validate(style) {
|
|
// mandatory fields
|
|
for (const prop of ['name', 'namespace', 'version']) {
|
|
if (!style[prop]) {
|
|
throw new Error(chrome.i18n.getMessage('styleMissingMeta', prop));
|
|
}
|
|
}
|
|
}
|
|
|
|
return {buildMeta, buildCode, semverTest};
|
|
})();
|