Files
snappymail/dev/Common/CSS.js
2023-02-14 14:54:38 +01:00

83 lines
2.0 KiB
JavaScript

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) => css.forEach(obj => {
if (obj.type === 'media') {
obj.subStyles.applyNamespace(namespace);
} else {
obj.selector = obj.selector.split(',').map(selector => namespace + ' ' + selector).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;
}
}