mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Bugfix: new CSS <style> removing (background-)?color failed
This commit is contained in:
parent
492f1d4b10
commit
bd9e50f222
2 changed files with 82 additions and 89 deletions
|
|
@ -1,84 +0,0 @@
|
||||||
|
|
||||||
export class CSS
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Parses given css string, and returns css object
|
|
||||||
keys as selectors and values are css rules
|
|
||||||
eliminates all css comments before parsing
|
|
||||||
|
|
||||||
@param source css string to be parsed
|
|
||||||
|
|
||||||
@return object css
|
|
||||||
*/
|
|
||||||
parse(source) {
|
|
||||||
|
|
||||||
const css = [];
|
|
||||||
|
|
||||||
css.toString = () => css.reduce(
|
|
||||||
(ret, tmp) =>
|
|
||||||
ret + tmp.selector + ' {\n'
|
|
||||||
+ (tmp.type === 'media' ? tmp.subStyles.toString() : tmp.rules)
|
|
||||||
+ '}\n'
|
|
||||||
,
|
|
||||||
''
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given css array, parses it and then for every selector,
|
|
||||||
* prepends namespace to prevent css collision issues
|
|
||||||
*/
|
|
||||||
css.applyNamespace = (namespace, prefix) => css.forEach(obj => {
|
|
||||||
if (obj.type === 'media') {
|
|
||||||
obj.subStyles.applyNamespace(namespace, prefix);
|
|
||||||
} else {
|
|
||||||
obj.selector = obj.selector.split(',').map(selector =>
|
|
||||||
namespace + ' ' + selector.replace('.', '.'+prefix)
|
|
||||||
).join(',');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (source) {
|
|
||||||
source = source
|
|
||||||
// strip comments
|
|
||||||
.replace(/\/\*[\s\S]*?\*\/|<!--|-->/gi, '')
|
|
||||||
// strip import statements
|
|
||||||
.replace(/@import .*?;/gi , '')
|
|
||||||
// strip keyframe statements
|
|
||||||
.replace(/((@.*?keyframes [\s\S]*?){([\s\S]*?}\s*?)})/gi, '');
|
|
||||||
|
|
||||||
// unified regex to match css & media queries together
|
|
||||||
let unified = /((\s*?(?:\/\*[\s\S]*?\*\/)?\s*?@media[\s\S]*?){([\s\S]*?)}\s*?})|(([\s\S]*?){([\s\S]*?)})/gi,
|
|
||||||
arr;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
arr = unified.exec(source);
|
|
||||||
if (arr === null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let selector = arr[arr[2] === undefined ? 5 : 2].split('\r\n').join('\n').trim()
|
|
||||||
// Never have more than a single line break in a row
|
|
||||||
.replace(/\n+/, "\n");
|
|
||||||
|
|
||||||
// determine the type
|
|
||||||
if (selector.includes('@media')) {
|
|
||||||
// we have a media query
|
|
||||||
css.push({
|
|
||||||
selector: selector,
|
|
||||||
type: 'media',
|
|
||||||
subStyles: this.parse(arr[3] + '\n}') //recursively parse media query inner css
|
|
||||||
});
|
|
||||||
} else if (!selector.includes('@') && ![':root','html','body'].includes(selector)) {
|
|
||||||
// we have standard css
|
|
||||||
css.push({
|
|
||||||
selector: selector,
|
|
||||||
rules: arr[6]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return css;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { createElement } from 'Common/Globals';
|
import { createElement } from 'Common/Globals';
|
||||||
import { CSS } from 'Common/CSS';
|
|
||||||
import { forEachObjectEntry, pInt } from 'Common/Utils';
|
import { forEachObjectEntry, pInt } from 'Common/Utils';
|
||||||
import { SettingsUserStore } from 'Stores/User/Settings';
|
import { SettingsUserStore } from 'Stores/User/Settings';
|
||||||
|
|
||||||
|
|
@ -89,6 +88,83 @@ const
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Parses given css string, and returns css object
|
||||||
|
keys as selectors and values are css rules
|
||||||
|
eliminates all css comments before parsing
|
||||||
|
|
||||||
|
@param source css string to be parsed
|
||||||
|
|
||||||
|
@return object css
|
||||||
|
*/
|
||||||
|
parseCSS = source => {
|
||||||
|
const css = [];
|
||||||
|
css.toString = () => css.reduce(
|
||||||
|
(ret, tmp) =>
|
||||||
|
ret + tmp.selector + ' {\n'
|
||||||
|
+ (tmp.type === 'media' ? tmp.subStyles.toString() : tmp.rules)
|
||||||
|
+ '}\n'
|
||||||
|
,
|
||||||
|
''
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* Given css array, parses it and then for every selector,
|
||||||
|
* prepends namespace to prevent css collision issues
|
||||||
|
*/
|
||||||
|
css.applyNamespace = (namespace, prefix) => css.forEach(obj => {
|
||||||
|
if (obj.type === 'media') {
|
||||||
|
obj.subStyles.applyNamespace(namespace, prefix);
|
||||||
|
} else {
|
||||||
|
obj.selector = obj.selector.split(',').map(selector =>
|
||||||
|
namespace + ' ' + selector.replace('.', '.'+prefix)
|
||||||
|
).join(',');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
source = source
|
||||||
|
// strip comments
|
||||||
|
.replace(/\/\*[\s\S]*?\*\/|<!--|-->/gi, '')
|
||||||
|
// strip import statements
|
||||||
|
.replace(/@import .*?;/gi , '')
|
||||||
|
// strip keyframe statements
|
||||||
|
.replace(/((@.*?keyframes [\s\S]*?){([\s\S]*?}\s*?)})/gi, '');
|
||||||
|
|
||||||
|
// unified regex to match css & media queries together
|
||||||
|
let unified = /((\s*?(?:\/\*[\s\S]*?\*\/)?\s*?@media[\s\S]*?){([\s\S]*?)}\s*?})|(([\s\S]*?){([\s\S]*?)})/gi,
|
||||||
|
arr;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
arr = unified.exec(source);
|
||||||
|
if (arr === null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let selector = arr[arr[2] === undefined ? 5 : 2].split('\r\n').join('\n').trim()
|
||||||
|
// Never have more than a single line break in a row
|
||||||
|
.replace(/\n+/, "\n");
|
||||||
|
|
||||||
|
// determine the type
|
||||||
|
if (selector.includes('@media')) {
|
||||||
|
// we have a media query
|
||||||
|
css.push({
|
||||||
|
selector: selector,
|
||||||
|
type: 'media',
|
||||||
|
subStyles: parseCSS(arr[3] + '\n}') //recursively parse media query inner css
|
||||||
|
});
|
||||||
|
} else if (!selector.includes('@') && ![':root','html','body'].includes(selector)) {
|
||||||
|
// we have standard css
|
||||||
|
css.push({
|
||||||
|
selector: selector,
|
||||||
|
rules: arr[6]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return css;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const
|
export const
|
||||||
|
|
@ -209,13 +285,14 @@ export const
|
||||||
oStyle = oElement.style;
|
oStyle = oElement.style;
|
||||||
|
|
||||||
if ('STYLE' === name) {
|
if ('STYLE' === name) {
|
||||||
if (msgId) {
|
let css = msgId ? parseCSS(oElement.textContent) : [];
|
||||||
let css = new CSS().parse(oElement.textContent);
|
if (css.length) {
|
||||||
css.applyNamespace(msgId, 'msg-');
|
css.applyNamespace(msgId, 'msg-');
|
||||||
oElement.textContent = css;
|
css = css.toString();
|
||||||
if (SettingsUserStore.removeColors()) {
|
if (SettingsUserStore.removeColors()) {
|
||||||
oElement.textContent = oElement.textContent.replace(/(background-)color:[^};]+/g, '');
|
css = css.replace(/(background-)?color:[^};]+;?/g, '');
|
||||||
}
|
}
|
||||||
|
oElement.textContent = css;
|
||||||
} else {
|
} else {
|
||||||
oElement.remove();
|
oElement.remove();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue