fdbfb23547
* parserlib: fast section extraction, tweaks and speedups
* csslint: "simple-not" rule
* csslint: enable and fix "selector-newline" rule
* simplify db: resolve with result
* simplify download()
* remove noCode param as it wastes more time/memory on copying
* styleManager: switch style<->data names to reflect their actual contents
* inline method bodies to avoid indirection and enable better autocomplete/hint/jump support in IDE
* upgrade getEventKeyName to handle mouse clicks
* don't trust location.href as it hides text fragment
* getAllKeys is implemented since Chrome48, FF44
* allow recoverable css errors + async'ify usercss.js
* openManage: unminimize windows
* remove the obsolete Chrome pre-65 workaround
* fix temporal dead zone in apply.js
* ff bug workaround for simple editor window
* consistent window scrolling in scrollToEditor and jumpToPos
* rework waitForSelector and collapsible <details>
* blank paint frame workaround for new Chrome
* extract stuff from edit.js and load on demand
* simplify regexpTester::isShown
* move MozDocMapper to sections-util.js
* extract fitSelectBox()
* initialize router earlier
* use helpPopup.close()
* fix autofocus in popups, follow-up to 5bb1b5ef
* clone objects in prefs.get() + cosmetics
* reuse getAll result for INC
153 lines
4.6 KiB
JavaScript
153 lines
4.6 KiB
JavaScript
/* global API */// msg.js
|
|
/* global URLS deepCopy download */// toolbox.js
|
|
'use strict';
|
|
|
|
const usercssMan = {
|
|
|
|
GLOBAL_META: Object.entries({
|
|
author: null,
|
|
description: null,
|
|
homepageURL: 'url',
|
|
updateURL: 'updateUrl',
|
|
name: null,
|
|
}),
|
|
|
|
async assignVars(style, oldStyle) {
|
|
const meta = style.usercssData;
|
|
const vars = meta.vars;
|
|
const oldVars = oldStyle.usercssData.vars;
|
|
if (vars && oldVars) {
|
|
// The type of var might be changed during the update. Set value to null if the value is invalid.
|
|
for (const [key, v] of Object.entries(vars)) {
|
|
const old = oldVars[key] && oldVars[key].value;
|
|
if (old) v.value = old;
|
|
}
|
|
meta.vars = await API.worker.nullifyInvalidVars(vars);
|
|
}
|
|
},
|
|
|
|
async build({
|
|
styleId,
|
|
sourceCode,
|
|
vars,
|
|
checkDup,
|
|
metaOnly,
|
|
assignVars,
|
|
initialUrl,
|
|
}) {
|
|
// downloading here while install-usercss page is loading to avoid the wait
|
|
if (initialUrl) sourceCode = await download(initialUrl);
|
|
const style = await usercssMan.buildMeta({sourceCode});
|
|
const dup = (checkDup || assignVars) &&
|
|
await usercssMan.find(styleId ? {id: styleId} : style);
|
|
if (!metaOnly) {
|
|
if (vars || assignVars) {
|
|
await usercssMan.assignVars(style, vars ? {usercssData: {vars}} : dup);
|
|
}
|
|
await usercssMan.buildCode(style);
|
|
}
|
|
return {style, dup};
|
|
},
|
|
|
|
async buildCode(style) {
|
|
const {sourceCode: code, usercssData: {vars, preprocessor}} = style;
|
|
const match = code.match(URLS.rxMETA);
|
|
const i = match.index;
|
|
const j = i + match[0].length;
|
|
const codeNoMeta = code.slice(0, i) + blankOut(code, i, j) + code.slice(j);
|
|
const {sections, errors} = await API.worker.compileUsercss(preprocessor, codeNoMeta, vars);
|
|
const recoverable = errors.every(e => e.recoverable);
|
|
if (!sections.length || !recoverable) {
|
|
throw !recoverable ? errors : 'Style does not contain any actual CSS to apply.';
|
|
}
|
|
style.sections = sections;
|
|
return style;
|
|
},
|
|
|
|
async buildMeta(style) {
|
|
if (style.usercssData) {
|
|
return style;
|
|
}
|
|
// remember normalized sourceCode
|
|
let code = style.sourceCode = style.sourceCode.replace(/\r\n?/g, '\n');
|
|
style = Object.assign({
|
|
enabled: true,
|
|
sections: [],
|
|
}, style);
|
|
const match = code.match(URLS.rxMETA);
|
|
if (!match) {
|
|
return Promise.reject(new Error('Could not find metadata.'));
|
|
}
|
|
try {
|
|
code = blankOut(code, 0, match.index) + match[0];
|
|
const {metadata} = await API.worker.parseUsercssMeta(code);
|
|
style.usercssData = metadata;
|
|
// https://github.com/openstyles/stylus/issues/560#issuecomment-440561196
|
|
for (const [key, globalKey] of usercssMan.GLOBAL_META) {
|
|
const val = metadata[key];
|
|
if (val !== undefined) {
|
|
style[globalKey || key] = val;
|
|
}
|
|
}
|
|
return style;
|
|
} catch (err) {
|
|
if (err.code) {
|
|
const args = err.code === 'missingMandatory' || err.code === 'missingChar'
|
|
? err.args.map(e => e.length === 1 ? JSON.stringify(e) : e).join(', ')
|
|
: err.args;
|
|
const msg = chrome.i18n.getMessage(`meta_${(err.code)}`, args);
|
|
if (msg) err.message = msg;
|
|
}
|
|
return Promise.reject(err);
|
|
}
|
|
},
|
|
|
|
async configVars(id, vars) {
|
|
const style = deepCopy(await API.styles.get(id));
|
|
style.usercssData.vars = vars;
|
|
await usercssMan.buildCode(style);
|
|
return (await API.styles.install(style, 'config'))
|
|
.usercssData.vars;
|
|
},
|
|
|
|
async editSave(style) {
|
|
return API.styles.editSave(await usercssMan.parse(style));
|
|
},
|
|
|
|
async find(styleOrData) {
|
|
if (styleOrData.id) {
|
|
return API.styles.get(styleOrData.id);
|
|
}
|
|
const {name, namespace} = styleOrData.usercssData || styleOrData;
|
|
for (const dup of await API.styles.getAll()) {
|
|
const data = dup.usercssData;
|
|
if (data &&
|
|
data.name === name &&
|
|
data.namespace === namespace) {
|
|
return dup;
|
|
}
|
|
}
|
|
},
|
|
|
|
async install(style) {
|
|
return API.styles.install(await usercssMan.parse(style));
|
|
},
|
|
|
|
async parse(style) {
|
|
style = await usercssMan.buildMeta(style);
|
|
// preserve style.vars during update
|
|
const dup = await usercssMan.find(style);
|
|
if (dup) {
|
|
style.id = dup.id;
|
|
await usercssMan.assignVars(style, dup);
|
|
}
|
|
return usercssMan.buildCode(style);
|
|
},
|
|
};
|
|
|
|
/** Replaces everything with spaces to keep the original length,
|
|
* but preserves the line breaks to keep the original line/col relation */
|
|
function blankOut(str, start = 0, end = str.length) {
|
|
return str.slice(start, end).replace(/[^\r\n]/g, ' ');
|
|
}
|