Change style structure
This commit is contained in:
parent
dc988a413e
commit
a0495f466f
|
@ -370,7 +370,7 @@ function filterStylesInternal({
|
|||
|
||||
|
||||
// Parse the source and find the duplication
|
||||
// {id: int, style: object, source: string, checkDup: boolean}
|
||||
// {id: int, style: object, sourceCode: string, checkDup: boolean}
|
||||
function filterUsercss(req) {
|
||||
let style;
|
||||
let pendingBuild;
|
||||
|
@ -381,8 +381,8 @@ function filterUsercss(req) {
|
|||
|
||||
function buildMeta() {
|
||||
return new Promise(resolve => {
|
||||
if (req.source) {
|
||||
style = usercss.buildMeta(req.source);
|
||||
if (req.sourceCode) {
|
||||
style = usercss.buildMeta(req.sourceCode);
|
||||
} else {
|
||||
style = req.style;
|
||||
}
|
||||
|
@ -426,8 +426,8 @@ function saveUsercss(style) {
|
|||
|
||||
function buildMeta() {
|
||||
return new Promise(resolve => {
|
||||
if (!style.name || !style.namespace) {
|
||||
resolve(Object.assign(usercss.buildMeta(style.source), style));
|
||||
if (!style.usercssData) {
|
||||
resolve(Object.assign(usercss.buildMeta(style.sourceCode), style));
|
||||
return;
|
||||
}
|
||||
resolve(style);
|
||||
|
@ -449,16 +449,22 @@ function saveStyle(style) {
|
|||
let existed;
|
||||
let codeIsUpdated;
|
||||
|
||||
if (style.usercss) {
|
||||
return processUsercss(style).then(decide);
|
||||
}
|
||||
const maybeProcess = style.usercssData ? processUsercss() : Promise.resolve();
|
||||
|
||||
return maybeProcess
|
||||
.then(maybeUpdate)
|
||||
.then(maybeImportFix)
|
||||
.then(decide);
|
||||
|
||||
function maybeUpdate() {
|
||||
if (reason === 'update' || reason === 'update-digest') {
|
||||
return calcStyleDigest(style).then(digest => {
|
||||
style.originalDigest = digest;
|
||||
return decide();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function maybeImportFix() {
|
||||
if (reason === 'import') {
|
||||
style.originalDigest = style.originalDigest || style.styleDigest; // TODO: remove in the future
|
||||
delete style.styleDigest; // TODO: remove in the future
|
||||
|
@ -466,9 +472,9 @@ function saveStyle(style) {
|
|||
delete style.originalDigest;
|
||||
}
|
||||
}
|
||||
return decide();
|
||||
}
|
||||
|
||||
function processUsercss(style) {
|
||||
function processUsercss() {
|
||||
return findDupUsercss(style)
|
||||
.then(dup => {
|
||||
if (!dup) {
|
||||
|
@ -477,11 +483,10 @@ function saveStyle(style) {
|
|||
if (!id) {
|
||||
id = dup.id;
|
||||
}
|
||||
if (reason === 'config') {
|
||||
return;
|
||||
}
|
||||
if (reason !== 'config') {
|
||||
// preserve style.vars during update
|
||||
usercss.assignVars(style, dup);
|
||||
}
|
||||
})
|
||||
.then(() => usercss.buildCode(style));
|
||||
}
|
||||
|
@ -538,7 +543,7 @@ function saveStyle(style) {
|
|||
style, codeIsUpdated, reason,
|
||||
});
|
||||
}
|
||||
if (style.usercss && !existed && reason === 'install') {
|
||||
if (style.usercssData && !existed && reason === 'update') {
|
||||
// open the editor for usercss with the first install?
|
||||
openEditor(style.id);
|
||||
}
|
||||
|
@ -563,7 +568,13 @@ function findDupUsercss(style) {
|
|||
return getStyles({id: style.id}).then(s => s[0]);
|
||||
}
|
||||
return getStyles().then(styles =>
|
||||
styles.find(s => s.name === style.name && s.namespace === style.namespace)
|
||||
styles.find(target => {
|
||||
if (!target.usercssData) {
|
||||
return false;
|
||||
}
|
||||
return target.usercssData.name === style.usercssData.name &&
|
||||
target.usercssData.namespace === style.usercssData.namespace;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -815,7 +826,8 @@ function normalizeStyleSections({sections}) {
|
|||
|
||||
|
||||
function calcStyleDigest(style) {
|
||||
const jsonString = JSON.stringify(normalizeStyleSections(style));
|
||||
const jsonString = style.usercssData ?
|
||||
style.sourceCode : JSON.stringify(normalizeStyleSections(style));
|
||||
const text = new TextEncoder('utf-8').encode(jsonString);
|
||||
return crypto.subtle.digest('SHA-1', text).then(hex);
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ var updater = {
|
|||
|
||||
'ignoreDigest' option is set on the second manual individual update check on the manage page.
|
||||
*/
|
||||
const maybeUpdate = style.usercss ? maybeUpdateUsercss : maybeUpdateUSO;
|
||||
const maybeUpdate = style.usercssData ? maybeUpdateUsercss : maybeUpdateUSO;
|
||||
return (ignoreDigest ? Promise.resolve() : calcStyleDigest(style))
|
||||
.then(checkIfEdited)
|
||||
.then(maybeUpdate)
|
||||
|
@ -83,9 +83,7 @@ var updater = {
|
|||
if (ignoreDigest) {
|
||||
return;
|
||||
}
|
||||
if (style.usercss && style.edited) {
|
||||
return Promise.reject(updater.EDITED);
|
||||
} else if (style.originalDigest && style.originalDigest !== digest) {
|
||||
if (style.originalDigest && style.originalDigest !== digest) {
|
||||
return Promise.reject(updater.EDITED);
|
||||
}
|
||||
}
|
||||
|
@ -106,12 +104,14 @@ var updater = {
|
|||
function maybeUpdateUsercss() {
|
||||
return download(style.updateUrl).then(text => {
|
||||
const json = usercss.buildMeta(text);
|
||||
const {usercssData: {version}} = style;
|
||||
const {usercssData: {version: newVersion}} = json;
|
||||
// re-install is invalid in a soft upgrade
|
||||
if (semverCompare(style.version, json.version) === 0 && !ignoreDigest) {
|
||||
if (semverCompare(version, newVersion) === 0 && !ignoreDigest) {
|
||||
return Promise.reject(updater.SAME_VERSION);
|
||||
}
|
||||
// downgrade is always invalid
|
||||
if (semverCompare(style.version, json.version) > 0) {
|
||||
if (semverCompare(version, newVersion) > 0) {
|
||||
return Promise.reject(updater.ERROR_VERSION);
|
||||
}
|
||||
return json;
|
||||
|
@ -121,7 +121,7 @@ var updater = {
|
|||
function maybeSave(json) {
|
||||
json.id = style.id;
|
||||
// no need to compare section code for usercss, they are built dynamically
|
||||
if (!json.usercss) {
|
||||
if (!json.usercssData) {
|
||||
if (!styleJSONseemsValid(json)) {
|
||||
return Promise.reject(updater.ERROR_JSON);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ let pendingResource;
|
|||
function install(style) {
|
||||
const request = Object.assign(style, {
|
||||
method: 'saveUsercss',
|
||||
reason: 'install',
|
||||
reason: 'update',
|
||||
updateUrl: location.href
|
||||
});
|
||||
return runtimeSend(request)
|
||||
|
@ -55,7 +55,9 @@ function getAppliesTo(style) {
|
|||
|
||||
function initInstallPage({style, dup}, sourceLoader) {
|
||||
return pendingResource.then(() => {
|
||||
const versionTest = dup && semverCompare(style.version, dup.version);
|
||||
const data = style.usercssData;
|
||||
const dupData = dup && dup.usercssData;
|
||||
const versionTest = dup && semverCompare(data.version, dupData.version);
|
||||
document.body.textContent = '';
|
||||
document.body.appendChild(buildPage());
|
||||
|
||||
|
@ -65,13 +67,15 @@ function initInstallPage({style, dup}, sourceLoader) {
|
|||
$('.actions')
|
||||
);
|
||||
}
|
||||
$('.code').textContent = style.source;
|
||||
$('.code').textContent = style.sourceCode;
|
||||
$('button.install').onclick = () => {
|
||||
if (dup) {
|
||||
if (confirm(chrome.i18n.getMessage('styleInstallOverwrite', [style.name, dup.version, style.version]))) {
|
||||
if (confirm(chrome.i18n.getMessage('styleInstallOverwrite', [
|
||||
data.name, dupData.version, data.version
|
||||
]))) {
|
||||
install(style);
|
||||
}
|
||||
} else if (confirm(chrome.i18n.getMessage('styleInstall', [style.name]))) {
|
||||
} else if (confirm(chrome.i18n.getMessage('styleInstall', [data.name]))) {
|
||||
install(style);
|
||||
}
|
||||
};
|
||||
|
@ -87,19 +91,19 @@ function initInstallPage({style, dup}, sourceLoader) {
|
|||
$element({tag: 'button', className: 'install', textContent: installButtonLabel()})
|
||||
]}),
|
||||
$element({tag: 'h1', appendChild: [
|
||||
style.name,
|
||||
$element({tag: 'small', className: 'meta-version', textContent: style.version})
|
||||
data.name,
|
||||
$element({tag: 'small', className: 'meta-version', textContent: data.version})
|
||||
]}),
|
||||
$element({tag: 'p', textContent: style.description}),
|
||||
style.author && $element({tag: 'h3', textContent: t('author')}),
|
||||
style.author,
|
||||
style.license && $element({tag: 'h3', textContent: t('license')}),
|
||||
style.license,
|
||||
$element({tag: 'p', textContent: data.description}),
|
||||
data.author && $element({tag: 'h3', textContent: t('author')}),
|
||||
data.author,
|
||||
data.license && $element({tag: 'h3', textContent: t('license')}),
|
||||
data.license,
|
||||
$element({tag: 'h3', textContent: t('appliesLabel')}),
|
||||
$element({tag: 'ul', appendChild: getAppliesTo(style).map(
|
||||
pattern => $element({tag: 'li', textContent: pattern})
|
||||
)}),
|
||||
externalLink(style),
|
||||
externalLink(),
|
||||
]}),
|
||||
$element({className: 'main', appendChild: [
|
||||
$element({className: 'code'})
|
||||
|
@ -107,13 +111,13 @@ function initInstallPage({style, dup}, sourceLoader) {
|
|||
]});
|
||||
}
|
||||
|
||||
function externalLink(style) {
|
||||
function externalLink() {
|
||||
const urls = [];
|
||||
if (style.url) {
|
||||
urls.push([style.url, t('externalHomepage')]);
|
||||
if (data.homepageURL) {
|
||||
urls.push([data.homepageURL, t('externalHomepage')]);
|
||||
}
|
||||
if (style.supportURL) {
|
||||
urls.push([style.supportURL, t('externalSupport')]);
|
||||
if (data.supportURL) {
|
||||
urls.push([data.supportURL, t('externalSupport')]);
|
||||
}
|
||||
if (urls.length) {
|
||||
return $element({appendChild: [
|
||||
|
@ -139,7 +143,7 @@ function initLiveReload(sourceLoader) {
|
|||
return runtimeSend({
|
||||
method: 'saveUsercss',
|
||||
id: installed.id,
|
||||
source: source
|
||||
sourceCode: source
|
||||
}).then(() => {
|
||||
$$('.main .warning').forEach(e => e.remove());
|
||||
}).catch(err => {
|
||||
|
@ -267,7 +271,7 @@ function initUsercssInstall() {
|
|||
.then(() =>
|
||||
runtimeSend({
|
||||
method: 'filterUsercss',
|
||||
source: sourceLoader.source(),
|
||||
sourceCode: sourceLoader.source(),
|
||||
checkDup: true
|
||||
})
|
||||
)
|
||||
|
|
|
@ -1352,7 +1352,7 @@ function setStyleMeta(style) {
|
|||
|
||||
function initWithStyle(request) {
|
||||
if (!editor) {
|
||||
if (!request.style.usercss) {
|
||||
if (!request.style.usercssData) {
|
||||
initWithSectionStyle(request);
|
||||
} else {
|
||||
editor = createSourceEditor(request.style);
|
||||
|
|
|
@ -26,7 +26,7 @@ function createSourceEditor(style) {
|
|||
);
|
||||
|
||||
// draw CodeMirror
|
||||
$('#sections textarea').value = style.source;
|
||||
$('#sections textarea').value = style.sourceCode;
|
||||
const cm = CodeMirror.fromTextArea($('#sections textarea'));
|
||||
// too many functions depend on this global
|
||||
editors.push(cm);
|
||||
|
@ -378,8 +378,8 @@ function createSourceEditor(style) {
|
|||
// source
|
||||
cm.on('change', () => {
|
||||
const value = cm.getValue();
|
||||
dirty.modify('source', style.source, value);
|
||||
style.source = value;
|
||||
dirty.modify('source', style.sourceCode, value);
|
||||
style.sourceCode = value;
|
||||
|
||||
updateLintReportIfEnabled(cm);
|
||||
});
|
||||
|
@ -402,10 +402,11 @@ function createSourceEditor(style) {
|
|||
$('#name').value = style.name;
|
||||
$('#enabled').checked = style.enabled;
|
||||
$('#url').href = style.url;
|
||||
cm.setOption('mode', MODE[style.preprocessor] || 'css');
|
||||
CodeMirror.autoLoadMode(cm, style.preprocessor || 'css');
|
||||
const {usercssData: {preprocessor}} = style;
|
||||
cm.setOption('mode', MODE[preprocessor] || 'css');
|
||||
CodeMirror.autoLoadMode(cm, MODE[preprocessor] || 'css');
|
||||
// beautify only works with regular CSS
|
||||
$('#beautify').disabled = Boolean(style.preprocessor);
|
||||
$('#beautify').disabled = MODE[preprocessor] && MODE[preprocessor] !== 'css';
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
|
@ -417,9 +418,9 @@ function createSourceEditor(style) {
|
|||
function replaceStyle(newStyle) {
|
||||
style = deepCopy(newStyle);
|
||||
updateMetas();
|
||||
if (style.source !== cm.getValue()) {
|
||||
if (style.sourceCode !== cm.getValue()) {
|
||||
const cursor = cm.getCursor();
|
||||
cm.setValue(style.source);
|
||||
cm.setValue(style.sourceCode);
|
||||
cm.setCursor(cursor);
|
||||
}
|
||||
dirty.clear();
|
||||
|
@ -448,8 +449,7 @@ function createSourceEditor(style) {
|
|||
reason: 'editSave',
|
||||
id: style.id,
|
||||
enabled: style.enabled,
|
||||
edited: dirty.has('source'),
|
||||
source: style.source
|
||||
sourceCode: style.sourceCode
|
||||
};
|
||||
return onBackgroundReady().then(() => BG.saveUsercss(req))
|
||||
.then(result => {
|
||||
|
|
111
js/usercss.js
111
js/usercss.js
|
@ -4,10 +4,23 @@
|
|||
|
||||
// eslint-disable-next-line no-var
|
||||
var usercss = (function () {
|
||||
const METAS = [
|
||||
'author', 'advanced', 'description', 'homepageURL', 'icon', 'license', 'name',
|
||||
'namespace', 'noframes', 'preprocessor', 'supportURL', 'var', 'version'
|
||||
];
|
||||
// true for global, false for private
|
||||
const METAS = {
|
||||
__proto__: null,
|
||||
author: true,
|
||||
advanced: false,
|
||||
description: true,
|
||||
homepageURL: false,
|
||||
// icon: false,
|
||||
license: false,
|
||||
name: true,
|
||||
namespace: false,
|
||||
// noframes: false,
|
||||
preprocessor: false,
|
||||
supportURL: false,
|
||||
'var': false,
|
||||
version: false
|
||||
};
|
||||
|
||||
const META_VARS = ['text', 'color', 'checkbox', 'select', 'dropdown', 'image'];
|
||||
|
||||
|
@ -222,7 +235,7 @@ var usercss = (function () {
|
|||
parseStringToEnd(state);
|
||||
result.default = state.value;
|
||||
}
|
||||
state.style.vars[result.name] = result;
|
||||
state.usercssData.vars[result.name] = result;
|
||||
}
|
||||
|
||||
function parseEOT(state) {
|
||||
|
@ -319,72 +332,71 @@ var usercss = (function () {
|
|||
return s;
|
||||
}
|
||||
|
||||
function _buildMeta(source) {
|
||||
const style = {
|
||||
name: null,
|
||||
usercss: true,
|
||||
version: null,
|
||||
source: source,
|
||||
edited: false,
|
||||
enabled: true,
|
||||
sections: [],
|
||||
vars: {},
|
||||
preprocessor: null,
|
||||
noframes: false
|
||||
function _buildMeta(sourceCode) {
|
||||
const usercssData = {
|
||||
vars: {}
|
||||
};
|
||||
|
||||
const text = getMetaSource(source);
|
||||
const style = {
|
||||
enabled: true,
|
||||
sourceCode,
|
||||
sections: [],
|
||||
usercssData
|
||||
};
|
||||
|
||||
const text = getMetaSource(sourceCode);
|
||||
const re = /@(\w+)\s+/mg;
|
||||
const state = {style, re, text};
|
||||
const state = {style, re, text, usercssData};
|
||||
|
||||
let match;
|
||||
while ((match = re.exec(text))) {
|
||||
state.key = match[1];
|
||||
if (!METAS.includes(state.key)) {
|
||||
if (!(state.key in METAS)) {
|
||||
continue;
|
||||
}
|
||||
if (state.key === 'noframes') {
|
||||
style.noframes = true;
|
||||
} else if (state.key === 'var' || state.key === 'advanced') {
|
||||
if (state.key === 'var' || state.key === 'advanced') {
|
||||
if (state.key === 'advanced') {
|
||||
state.maybeUSO = true;
|
||||
}
|
||||
parseVar(state);
|
||||
} else {
|
||||
parseStringToEnd(state);
|
||||
if (state.key === 'homepageURL') {
|
||||
style.url = state.value;
|
||||
} else {
|
||||
style[state.key] = state.value;
|
||||
usercssData[state.key] = state.value;
|
||||
}
|
||||
if (METAS[state.key]) {
|
||||
style[state.key] = usercssData[state.key];
|
||||
}
|
||||
}
|
||||
if (state.maybeUSO && !usercssData.preprocessor) {
|
||||
usercssData.preprocessor = 'uso';
|
||||
}
|
||||
if (state.maybeUSO && !style.preprocessor) {
|
||||
style.preprocessor = 'uso';
|
||||
if (usercssData.homepageURL) {
|
||||
style.url = usercssData.homepageURL;
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
function buildCode(style) {
|
||||
const {usercssData: {preprocessor, vars}, sourceCode} = style;
|
||||
let builder;
|
||||
if (style.preprocessor) {
|
||||
if (!BUILDER.hasOwnProperty(style.preprocessor)) {
|
||||
return Promise.reject(new Error(`Unsupported preprocessor: ${style.preprocessor}`));
|
||||
if (preprocessor) {
|
||||
if (!BUILDER[preprocessor]) {
|
||||
return Promise.reject(new Error(`Unsupported preprocessor: ${preprocessor}`));
|
||||
}
|
||||
builder = BUILDER[style.preprocessor];
|
||||
builder = BUILDER[preprocessor];
|
||||
} else {
|
||||
builder = BUILDER.default;
|
||||
}
|
||||
|
||||
const vars = simpleVars(style.vars);
|
||||
const sVars = simpleVars(vars);
|
||||
|
||||
return Promise.resolve().then(() => {
|
||||
// preprocess
|
||||
if (builder.preprocess) {
|
||||
return builder.preprocess(style.source, vars);
|
||||
return builder.preprocess(sourceCode, sVars);
|
||||
}
|
||||
return style.source;
|
||||
return sourceCode;
|
||||
}).then(mozStyle =>
|
||||
// moz-parser
|
||||
loadScript('/js/moz-parser.js').then(() =>
|
||||
|
@ -395,7 +407,7 @@ var usercss = (function () {
|
|||
).then(() => {
|
||||
// postprocess
|
||||
if (builder.postprocess) {
|
||||
return builder.postprocess(style.sections, vars);
|
||||
return builder.postprocess(style.sections, sVars);
|
||||
}
|
||||
}).then(() => style);
|
||||
}
|
||||
|
@ -414,22 +426,23 @@ var usercss = (function () {
|
|||
}
|
||||
|
||||
function validate(style) {
|
||||
const {usercssData: data} = style;
|
||||
// mandatory fields
|
||||
for (const prop of ['name', 'namespace', 'version']) {
|
||||
if (!style[prop]) {
|
||||
if (!data[prop]) {
|
||||
throw new Error(chrome.i18n.getMessage('styleMissingMeta', prop));
|
||||
}
|
||||
}
|
||||
// validate version
|
||||
semverCompare(style.version, '0.0.0');
|
||||
semverCompare(data.version, '0.0.0');
|
||||
|
||||
// validate URLs
|
||||
validUrl(style.url);
|
||||
validUrl(style.supportURL);
|
||||
validUrl(data.homepageURL);
|
||||
validUrl(data.supportURL);
|
||||
|
||||
// validate vars
|
||||
for (const key of Object.keys(style.vars)) {
|
||||
validVar(style.vars[key]);
|
||||
for (const key of Object.keys(data.vars)) {
|
||||
validVar(data.vars[key]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,14 +469,16 @@ var usercss = (function () {
|
|||
}
|
||||
|
||||
function assignVars(style, old) {
|
||||
const {usercssData: {vars}} = style;
|
||||
const {usercssData: {vars: oldVars}} = old;
|
||||
// The type of var might be changed during the update. Set value to null if the value is invalid.
|
||||
for (const key of Object.keys(style.vars)) {
|
||||
if (old.vars[key] && old.vars[key].value) {
|
||||
style.vars[key].value = old.vars[key].value;
|
||||
for (const key of Object.keys(vars)) {
|
||||
if (oldVars[key] && oldVars[key].value) {
|
||||
vars[key].value = oldVars[key].value;
|
||||
try {
|
||||
validVar(style.vars[key], 'value');
|
||||
validVar(vars[key], 'value');
|
||||
} catch (e) {
|
||||
style.vars[key].value = null;
|
||||
vars[key].value = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ function configDialog(style) {
|
|||
|
||||
function buildConfigForm() {
|
||||
const labels = [];
|
||||
const vars = deepCopy(style.vars);
|
||||
const vars = deepCopy(style.usercssData.vars);
|
||||
for (const key of Object.keys(vars)) {
|
||||
const va = vars[key];
|
||||
let appendChild;
|
||||
|
|
|
@ -193,7 +193,7 @@ function createStyleElement({style, name}) {
|
|||
if (style.updateUrl && newUI.enabled) {
|
||||
$('.actions', entry).appendChild(template.updaterIcons.cloneNode(true));
|
||||
}
|
||||
if (style.vars && Object.keys(style.vars).length && newUI.enabled) {
|
||||
if (shouldShowConfig() && newUI.enabled) {
|
||||
$('.actions', entry).appendChild(template.configureIcon.cloneNode(true));
|
||||
}
|
||||
|
||||
|
@ -202,6 +202,13 @@ function createStyleElement({style, name}) {
|
|||
createStyleTargetsElement({entry, style, postponeFavicons: name});
|
||||
|
||||
return entry;
|
||||
|
||||
function shouldShowConfig() {
|
||||
if (!style.usercssData) {
|
||||
return false;
|
||||
}
|
||||
return Object.keys(style.usercssData.vars).length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -293,7 +300,7 @@ Object.assign(handleEvent, {
|
|||
}
|
||||
style.reason = 'config';
|
||||
for (const key of keys) {
|
||||
style.vars[key].value = vars[key].value;
|
||||
style.usercssData.vars[key].value = vars[key].value;
|
||||
}
|
||||
saveStyleSafe(style);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user