I have a string with the following css that I need to process with javascript
h1
{
color: red;
}
.info
{
border: 1px dotted blue;
padding: 10px;
}
#rect
{
background: pink;
}
h2,
h3,
h4
{
font-weight: bold;
}
table td:last td
{
background: gainsboro;
}
How can I add the prefix .page
to each rule so the css doesn't break?
I want this result
.page h1
{
color: red;
}
.page .info
{
border: 1px dotted blue;
padding: 10px;
}
...
Right now, I solve it with looking for the indentation, but the code fails on this case
h1
{
color: red;
}
Which then ends up with
.page h1
{
.page color: red;
}
I could also look for rows that only has brackets, but then this case would fail
h1 { color: red; }
I don't want to build my own css parser and the ones I found mostly handles css applied to elements in the DOM and not strings with css. Is there a good parser or can this be achieved otherwise?
Here is a function to prefix all selectors with a class name. This function properly handle the @ rules:
var prefixCssSelectors = function(rules, className) {
var classLen = className.length,
char, nextChar, isAt, isIn;
// makes sure the className will not concatenate the selector
className += ' ';
// removes comments
rules = rules.replace( /\/\*(?:(?!\*\/)[\s\S])*\*\/|[\r\n\t]+/g, '' );
// makes sure nextChar will not target a space
rules = rules.replace( /}(\s*)@/g, '}@' );
rules = rules.replace( /}(\s*)}/g, '}}' );
for (var i = 0; i < rules.length-2; i++) {
char = rules[i];
nextChar = rules[i+1];
if (char === '@' && nextChar !== 'f') isAt = true;
if (!isAt && char === '{') isIn = true;
if (isIn && char === '}') isIn = false;
if (
!isIn &&
nextChar !== '@' &&
nextChar !== '}' &&
(
char === '}' ||
char === ',' ||
((char === '{' || char === ';') && isAt)
)
) {
rules = rules.slice(0, i+1) + className + rules.slice(i+1);
i += classLen;
isAt = false;
}
};
// prefix the first select if it is not `@media` and if it is not yet prefixed
if (rules.indexOf(className) !== 0 && rules.indexOf('@') !== 0) rules = className+rules;
return rules;
}
// Some examples:
console.log(prefixCssSelectors('div { width: 100%; }', '.page'));
console.log(prefixCssSelectors('@charset "utf-8"; div { width: 100%; }', '.page'));
console.log(prefixCssSelectors('@media only screen { div { width: 100%; } p { size: 1.2rem; } } @media only print { p { size: 1.2rem; } } div { height: 100%; font-family: "Arial", Times; }', '.page'));
console.log(prefixCssSelectors('@font-face { font-family: "Open Sans"; src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"); } div { width: 100%; }', '.page'));
If you only want to have the class name for the html
and body
selectors, add this:
rules = rules.replace(/( html| body)/g, '');